iOS 11 beta NSJSONSerialization issue with floats

Originator:dkulemalin
Number:rdar://34032848 Date Originated:08/23/17
Status:CLOSED Resolved:NO
Product:iOS Product Version:11.0
Classification:Other Reproducible:100%
 
Area:
Foundation

Summary:
NSJSONSerialization incorrectly serializes numbers of type double/float on iOS 11.

Steps to Reproduce:
Preconditions:
xCode version 9.0 beta 4
iOS simulator versions: iPad Air 2 (11.0).

Steps to reproduce:
1. Create NSDictionary with float value(0.45f in my case) converted to NSNumber.
2. Serialize dictionary to NSData using NSJSONSerialization
3. Convert NSData to NSString. 

Here is test code:

NSMutableDictionary *dict = [NSMutableDictionary new];
[dict setObject:[NSNumber numberWithFloat:0.45f] forKey:@"test"];
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(@"json: %@", jsonString);

Actual result:
json: {"test":0.44999998807907104}

Expected result:
json: {"test":0.45}

Notes:
1. The same issue with double (console output: "{"test":0.45000000000000001}")
2. Issue is reproducible on iOS11 device only. In configuration "xCode version 9.0 beta 4" + "iPad Air 2 (10.1)." everything works fine.

Comments

Finally got clarification that it works as intended. Very sad, because it is not acceptable for me.

Official response: This is expected behavior in iOS 11 and macOS High Sierra. In this release, NSJSONSerialization has expanded the range of numbers it can encode and decode to the full spectrum of float/double values (whereas before there were limits on the magnitude of the numbers it could encode and decode). In order to do this, NSJSONSerialization now outputs numbers with the full precision required to represent them. If you take a look at an IEEE-754 floating-point number converter like https://www.h-schmidt.net/FloatConverter/IEEE754.html, you can see that the value 4.45 is not representable exactly as a float or a double — its exact value is 4.44999980926513671875 as a float, for instance, which is what you see in the output above. Regardless of the string representation of the value, however, the number is guaranteed to round-trip to an equal value: NSArray value = @[@4.45]; NSLog(@"%@", value); NSData data = [NSJSONSerialization dataWithJSONObject:value options:0 error:nil]; NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); NSArray *decoded = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; NSLog(@"%@", decoded); NSLog(@"Equal: %@", [value isEqual:decoded] ? @"yes" : @"no"); You will not lose equality when round-tripping numbers like this.

By dkulemalin at Aug. 29, 2017, 7:54 a.m. (reply...)

This bug has been closed as duplicate of 33396000(which is in CLOSED state) on bugreport.apple.com without any comments. And it is not clear if Apple is planning to fix it or not.

By dkulemalin at Aug. 25, 2017, 10:58 a.m. (reply...)

Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!