[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: isEqual: and hash in NSDate
From: |
Richard Frith-Macdonald |
Subject: |
Re: isEqual: and hash in NSDate |
Date: |
Fri, 22 Jul 2005 16:26:15 +0100 |
On 2005-07-22 15:57:03 +0100 Adrian Robert <address@hidden> wrote:
>
> On Jul 21, 2005, at 3:49 PM, Jeremy Bettis wrote:
>
>> I have a problem with the implementations of isEqual: and hash in NSDate.
>>
>> Let's say we have two dates:
>>
>> NSDate *a, *b;
>> // This is actually a common case if you are doing floating point math to
>> generate dates and get small rounding errors.
>> a = [NSDate dateWithTimeIntervalSinceReferenceDate:100000.001];
>> b = [NSDate dateWithTimeIntervalSinceReferenceDate:99999.998];
>>
>> printf("a = %d, b=%d, equal=%d\n", [a hash] , [b hash], [a isEqual:b]);
>>
>> // this code will print a = 100000, b = 99999, equal = 1
>>
>> This breaks the NSDictionary rule that hash of two objects must equal if
>> they are -isEqual:.
>>
>> I propose that we change the implementations to this:
>> - (unsigned) hash
>> {
>> return (unsigned)([self timeIntervalSinceReferenceDate]+0.5);
>> }
>> - (BOOL) isEqual: (id)other
>> {
>> if (other == nil)
>> return NO;
>> if ([other isKindOfClass: abstractClass]
>> && (int)(otherTime(self)+0.5) == (int)(otherTime(other)+0.5) )
>> return YES;
>> return NO;
>> }
>> After my change the program's output changes to:
>> a = 100000, b = 100000, equal = 1
>>
>> I realize that the dates 100.5 and 100.49 are now not -isEqual:, but you
>> have to draw the line somewhere, and putting it at .000 as the old code was
>> worse.
>
> This really seems like a hack (not that it's any worse than the current state
> :). Couldn't the implementations of -hash and -isEqual be aligned without
> this loss of information? E.g. something like [warning, sloppy first-attempt
> code here]:
>
> -isEqual:other
> return abs(selfVal - other->selfVal) < epsilon;
>
> -hash
> return selfVal / epsilon;
> (or maybe (selfVal + epsilon/2.0) / epsilon)
>
> I think the problem with this is the division in -hash would need to be
> carried out at a higher precision than the floating point representation of
> selfVal uses. But maybe this could be worked around or lived with somehow?
> E.g., if we used 8-byte floating point, epsilon was 0.001 (1 msec) and we
> were willing to accept inaccuracy for dates beyond 100000 AD, would it work
> then? Unfortunately my numerical computation class was WAY too long ago..
I agree that the current code is poor ... the implementation of -hash and
-isEqual: must be compatibile with each other for a date to be used as a
dictionary key, so you can't currently use dates as dictionary keys.
However, I just checked with MacOS-X and the -isEqualToDate: method is
explicitly documented as performing an exact comparison rather than just to the
second.
The original OpenStep specification on the other hand explicitly states that
dates are equal if they are within a second of each other.
Now, the OpenStep specification therefore rules out the possibility of using
dates as dictionary keys.
I therefore propose to follow MacOS-X, leaving the implementation of -hash as
it is, and changing -isEqual: and -isEqualToDate: to perform exact comparisons.
That allows us to use dates as dictionary keys and gives us MacOS-X
compatibility.
Comments?