Loss of precision converting 'float' to NSNumber, back to 'float'
Asked Answered
E

4

9

I seem to be encountering a strange issue in Objective-C converting a float to an NSNumber (wrapping it for convenience) and then converting it back to a float.

In a nutshell, a class of mine has a property red, which is a float from 0.0 to 1.0:

@property (nonatomic, assign) float red;

This object is comparing itself to a value that is loaded from disk, for synchronization purposes. (The file can change outside the application, so it checks periodically for file changes, loads the alternate version into memory, and does a comparison, merging differences.)

Here's an interesting snippet where the two values are compared:

if (localObject.red != remoteObject.red) {
    NSLog(@"Local red: %f Remote red: %f", localObject.red, remoteObject.red);
}

Here's what I see in the logs:

2011-10-28 21:07:02.356 MyApp[12826:aa63] Local red: 0.205837 Remote red: 0.205837

Weird. Right? How is this piece of code being executed?

The actual value as stored in the file:

...red="0.205837"...

Is converted to a float using:

currentObject.red = [[attributeDict valueForKey:@"red"] floatValue];

At another point in the code I was able to snag a screenshot from GDB. It was printed to NSLog as: (This is also the precision with which it appears in the file on disk.)

2011-10-28 21:21:19.894 MyApp[13214:1c03] Local red: 0.707199 Remote red: 0.707199

But appears in the debugger as:

GDB Screenshot

How is this level of precision being obtained at the property level, but not stored in the file, or printed properly in NSLog? And why does it seem to be varying?

Epitaph answered 29/10, 2011 at 1:26 Comment(10)
Might have found something here, but it didn't pop up in any previous searches unfortunately: #5025413Epitaph
Are you converting it to/from a string at any point? If so, use %+0.16f instead of %f. Or whatever precision you want instead of .16. If not, disregard this comment ).Signorina
Yea, I'm absolutely converting it to/from an NSString. I had assumed though, that creating it: [NSString stringWithFormat:@"%f", 0.707199f] then converting it like: [... floatValue] would result in the same value. I guess not. Thanks for the tip, I imagine that will clear things up. Feel free to post it as an answer.Epitaph
Cool, let me know though if that doesn't fix it so I can delete my answer ). Hope it helps though.Signorina
To be clear - does that +0.16 precision round to the nearest 0.16? (Eg. would it round 0.35 to 0.32?) I found an example here, but it isn't really explained, under Formatting Basics: developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/…Epitaph
If the number is originaly 3.14159 exactly, the string would be @"3.14159" (using "%0.16f"), but if you had "%0.4f", then it would round to @"3.1416".Signorina
I'm having trouble seeing the link between the precision in the formatting string, and the precision that results in the returned float value. Sorry if I'm being dense. :)Epitaph
@MJD has a good explanation in his answer for the why part of your question.Signorina
@craig -- Look up "printf formatting" on the web. %0.16f means that 16 digits of fraction (to the right of the decimal point) should be printed. The "0" means that the overall field is AT LEAST zero spaces wide, but the overall field size will always accommodate all the fraction digits plus whatever integer (to the left of the decimal point) digits are needed to represent the number. A %5.2f field would represent 9.8765 as " 9.88" with the leading blank to make the field the required 5 characters wide.Loot
@DanielRHicks Perfect, that explains it very well. Thanks!Epitaph
S
5

If you are converting it to/from a string at any point try using %0.16f instead of %f (or whatever precision you want instead of .16).

For more info, see IEEE Std formatting.


Also, use objectForKey instead of valueForKey (valueForKey is not intended to be used on dictionaries):

currentObject.red = [[attributeDict objectForKey:@"red"] floatValue];

See this SO answer for a better explanation of objectForKey vs valueForKey:

Difference between objectForKey and valueForKey?

Signorina answered 29/10, 2011 at 1:42 Comment(2)
Thanks, that works! Also thanks for the objectForKey: note, I usually keep a closer eye on those things.Epitaph
@craig Np, glad I could help!Signorina
G
4

The problem your are experiencing is a problem with floating point. A floating point number doesn't exactly represent the number stored (except for some specific cases which don't matter here). The example in the link craig posted is an excellent example of this.

In your code, when you write out the value to your file you write an approximation of what is stored in the floating point number. When you load it back, another approximation of it is stored in the float. However these two numbers are unlikely to be equal.

The best solution is to use a fuzzy comparison of the two floating point numbers. I'm not an objective c programmer, so I don't know if the languages includes builtin functions to preform this comparison. However this link provides a good set of examples on various ways to preform this comparison.

You can also try the other posted solution of using a bigger precision to write out to your file, but you will probably end up wasting space for the extra precision that you don't need. I'd personally recommend you use the fuzzy comparison as it is more bullet proof.

Girth answered 29/10, 2011 at 1:54 Comment(0)
L
2

You say that the "remote" value is "loaded from disk". I'm guessing that the representation on disk is not an IEEE float bit value, but rather some sort of character representation or some such. So there are inevitable conversion errors going to and from that representation, given the way IEEE float works. You will not get an exact result, given that there's only about 6 digits of decimal precision in a float value, but it rarely maps to exactly 6 decimal digits but instead is sort of like representing 1/3 in decimal -- there is no exact mapping.

Loot answered 29/10, 2011 at 2:25 Comment(0)
J
0

Read this: http://floating-point-gui.de/

Jailbreak answered 29/10, 2011 at 2:14 Comment(1)
While this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.Wheelsman

© 2022 - 2024 — McMap. All rights reserved.