iPhone - Storing a double value into a NSDictionary with only 7 decimals as a Number type
I have a PList that contains a dictionary with some decimal values like 10.1234567, stored as Number type (tag ).
In my a开发者_如何学JAVApp, I read that plist and manipulate those values as NSNumber (double type values) with a custom class and its accessors :@interface
...
@property(nonatomic, assign) NSNumber* someValue;
...
@end
@implementation
...
- (NSNumber*) someValue { return [self.dict objectForKey:@"val"]; }
- (void) setSomeValue:(NSNumber*)val { [self.dict setValue:val forKey:@"val"]; }
...
@end
Then in some case, I write back on the disk the plist.
I've notice a few hours ago that those values where, let's say, converted and are now something like 10.123456699999999. All of them ! ...
Woaw, I didn't expected that change.
Well, I've tried to contain the problem changing the accessors with many thing, like for example this one :
- (NSNumber*) someValue { return [self.dict objectForKey:@"val"]; }
- (void) setSomeValue:(NSNumber*)val { [self.dict setValue:
[NSNumber numberWithDouble:[[NSString stringWithFormat:@"%.7lf", [val doubleValue]] doubleValue]]
forKey:@"val"]; }
But I can't succeed keeping those value stay with their original 7 decimals.
How may I do to write only 7 decimals, without having to use something else than a Number type for the field into the dictionary (I mean, using a String wouldn't be a good solution) ?
The creation of the plist file was made by hand, with some hours of typing, typing those 7 decimal values into each field, and they weren't "converted" at that moment. They were stored with 7 decimal. So I guess there is a solution to keep those 7 decimals when writing them back by program.
Welcome to the wonderful world of floating-point. If you want to use floating-point values you have to be prepared for the fact that floating-point values are only rarely exactly the values you want, but more often a hair more or less.
You can use an integer (scaled as necessary), or convert to character form. There is an NSDecimalNumber class that would be precise, but it does not fit into the NSNumber scheme and will not, to my knowledge, work with JSON, et al, if that were necessary. Should work as a dictionary key though, I'd think.
You might want to look into IEEE standard 754-2008, and brush up on your binary. Computers don't store floating point internally using base-10 digits 0 through 9; rather, they use binary numbers 0 and 1. If you feed 10.1234567 into a computer and ask it to store that number as a 64-bit double floating-point number, the closest it can get to that value will be something like the 10.1234566999999.... that you see. And that number is actually something like 1001.00111111001101011011011 (ie, 10 + 2071259/16777216).
If you want to store exactly 7 decimal digits, then store the raw characters as a string. Or come up with some other way of storing base-10 digits, instead of base-2 binary digits.
If you want to convert a floating-point number to text with only 7 digits of precision, you can use:
NSString* output = [NSString stringWithFormat:@"%.7d",myFloat];
Use NSNumberFormatter when you extract the NSNumber from the plist and it should work:
NSNumberFormatter *nf = [[[NSNumberFormatter alloc] init] autorelease];
[nf setMaximumFractionDigits:7];
NSNumber *yourNumber = [nf numberFromString:[[self someValue] stringValue]];
精彩评论