In my iOS app, I have a shape class, built with CGPoints. I save it to a file using encodeCGPoint:forKey. I read it back in. That all works.
However, the CGPoint values I read in are not exactly equal to the values I saved it. The low bits of the CGFloat values aren't stable. So CGPointEqualToPoint returns NO, which means my isEqual method returns NO. This causes me trouble and pain.
Obviously, serializing floats precisely has been a hassle since the beginning of time. But in this situation, what is the best approach? I can think of several:
- write out the x and y values using encodeFloat instead of encodeCGPoint (would that help at all?)
- multiply my x and y values by 256.0 before saving them (they're all going to be between -1 and 1, roughly, so this might help?)
- write out the x and y values using encodeDouble instead of encodeCGPoint (still might round the lowest bit incorrectly?)
- cast to NSUInteger and write them out using encodeInt32 (icky, but it would work, right?)
- accept the loss of precision, and implement my isEqual method to use within-epsilon comparison rather than CGPointEqualToPoint (sigh)
EDIT-ADD: So the second half of the problem, which I was leaving out for simplicity, is that I have to implement the hash method for these shape objects.
Hashing floats is also a horrible pain (see " Good way to hash a float vector? "), and it turns out it more or less nullifies my question. The toolkit's encodeCGPoint method rounds its float values in an annoying way -- it's literally printing them to a string with the %g format -- so there's no way I can use it and still make hashing reliable.
Therefore, I'm forced to write my own encodePoint function. As long as I'm doing that, I might as well write one that encodes the value exactly. (Copy two 32-bit floats into a 64-bit integer field, and no, it's not portable, but this is iOS-only and I'm making that tradeoff.)
With reliable exact storage of CGPoints, I can go back to exact comparison and any old hash function I want. Tolerance ranges do nothing for me, so I'm just not using them for this application.
If I wanted hashing and tolerance comparisons, I'd be comparing values within a tolerance of N significant figures, not a fixed distance epsilon. (That is, I'd want 0.123456 to compare close to 0.123457, but I'd also want 1234.56 to compare close to 1234.57.) That would be stable against floating-point math errors, for both large and small values. I don't have sample code for that, but start with the frexpf() function and it shouldn't be too hard.