I have a double
in an NSNumber
.
double myDouble = 1363395572.6129999;
NSNumber *doubleNumber = @(myDouble);
// using [NSNumber numberWithDouble:myDouble] leads to the same result
This is where it gets problematic.
doubleNumber.doubleValue
seems to return the correct and full value (1363395572.6129999)
However, looking at doubleNumber
in the debugger or doing doubleNumber.description
gives me (1363395572.613)
.
I would understand if perhaps this was just some display formatting, but when I then stick this object into a JSON payload, the messed up rounded value gets inserted instead of the actual number.
The way I'm doing this is something like this:
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:(Dictionary containing NSNumber)
options:0 error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
Looking at the string at this point shows me the truncated number with 3 decimal places even though the NSNumber
I inserted had 7.
My question is why is this happening and more importantly how can I stop it from happening?
EDIT with conclusion:
For anyone who stumbles onto this, the problem was not clear to me from the beginning but the actual issue is that NSNumber
and double
are both incapable of holding a number with the sort of precision I am looking for. As Martin's answer shows, my problem occurred as soon as I deserialized the initial number values from a JSON response.
I ended up working around my problem by reworking the whole system to stop depending on this level of precision(since these are timestamps, microseconds) of these numbers on the client, and instead use a different identifier to pass around with the API.
As Martin and Leo pointed out, in order to get around this problem one would need to use a custom JSON parser that allows parsing of a JSON number into an NSDecimalNumber
rather than an NSNumber
. A better solution to my problem in particular was what I outlined in the previous paragraph, so I did not pursue this route.
As already said in above comments, the precision of double
is about 16 decimal
digits. 1363395572.612999
has 17 digits, and converting this decimal number
to double
gives exactly the same results as for 1363395572.613
:
double myDouble = 1363395572.6129999;
double myDouble1 = 1363395572.613;
NSLog(@"%.20f", myDouble); // 1363395572.61299991607666015625
NSLog(@"%.20f", myDouble1); // 1363395572.61299991607666015625
NSLog(@"%s", myDouble == myDouble1 ? "equal" : "different"); // equal
Therefore, within the precision of double
, the output 1363395572.613
is correct.
If your goal is to send precisely the number "1363395572.6129999" then you cannot
store it in a double
first because that already looses the precision.
A possible solution would be to use NSDecimalNumber
(which has a precision
of 38 decimal digits):
NSDecimalNumber *doubleNumber = [NSDecimalNumber decimalNumberWithString:@"1363395572.6129999"];
NSDictionary *dict = @{@"key": doubleNumber};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:0 error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// {"key":1363395572.6129999}
Example with long double
and NSDecimalNumber
:
long double ld1 = 1363395572.6129999L;
long double ld2 = 1363395572.613L;
NSDecimalNumber *num1 = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%.7Lf", ld1]];
NSDecimalNumber *num2 = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%.7Lf", ld2]];
NSDictionary *dict = @{@"key1": num1, @"key2": num2};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:0 error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// {"key1":1363395572.6129999,"key2":1363395572.613}
Update: As it turned out in the discussion, the problem occurs already when the data
is read from a JSON object sent by a server. The following example shows that
NSJSONSerialization
is not able to read floating point numbers with more than
"double" precision from JSON data:
NSString *jsonString = @"{\"key1\":1363395572.6129999,\"key2\":1363395572.613}";
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dict2 = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:NULL];
NSNumber *n1 = dict2[@"key1"];
NSNumber *n2 = dict2[@"key2"];
BOOL b = [n1 isEqualTo:n2]; // YES
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With