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.
double
can only store about 13 significant digits. – Homeostasisdescription
is only to be used for debugging, and should never (except for a handful of object types) be counted on to accurately reflect the contents of the object. – Shorelessdouble
can hold nearly 16 significant decimal digits. – Shorelessdouble
shows the correct value at all times and never appears truncated. I can pass it around and use the full value in its primitive form without any issues. – Grumousdouble
was about 13 digits. Maybe that idea came about from seeing the same behavior seen here. – HomeostasisNSNumber *doubleNumber = [NSNumber numberWithDouble:myDouble];
and see if it makes a difference. I've seen the@()
notation causing issues by selecting the incorrect internalNSNumber
subclass. – Demars@
notation, create the NSNumber the "old fashioned" way. – ShorelessNSNumber
is actually storing the correct value. I see that it is adouble
in the debugger and when I printdoubleValue
it is indeed correct. – Grumousdouble
? If so, then it only has about 15.8 digits of precision, and you're expecting 17. If you want exact, don't use a float, but use a scaled long. (But of course if you're starting with an NSTimeInterval, double precision is all you get.) – Shoreless