`NSDecimalNumber`s from `NSNumberFormatter` are affected by binary approximation error

Originator:kamil.borzym
Number:rdar://29923468 Date Originated:09-Jan-2017 09:17 AM
Status:Open Resolved:
Product:Foundation Product Version:
Classification: Reproducible:Always
 
Area:
Foundation

Summary:
`NSNumberFormatter.generatesDecimalNumbers = YES` makes `-[NSNumberFormatter numberFromString:]` producing `NSDecimalNumber` instead of `NSNumber`. Unfortunately this is merely a class rewrapping, does not change actual number parsing, so decimal numbers are affected by binary approximation error.

In case of numbers parsing, `-[NSNumberFormatter numberFromString:]` uses `CFNumberFormatterCreateNumberFromString` which in turn uses `cficu_unum_parseDecimal`, then `strtod` and then `CFNumberCreate` to create `CFNumber` wrapped double type. At the end, when `NSNumberFormatter.generatesDecimalNumbers == YES`, `NSNumberFormatter` executes `[NSDecimalNumber decimalNumberWithDecimal:number.decimalValue]` which is merely a class rewrapping – NSDecimalNumber stores decimal representation of binary approximation, not a true decimal representation of actual number.

Steps to Reproduce:
```
NSNumberFormatter *fmt = [[NSNumberFormatter alloc] init];
fmt.generatesDecimalNumbers = YES;
fmt.locale = [NSLocale localeWithLocaleIdentifier:@"en_US"];
NSDecimalNumber *number = (NSDecimalNumber *)[fmt numberFromString:@"99.9"];
NSString *value = [number stringValue]; // "99.90000000000001", should be "99.9"
NSDecimal decimal = [number decimalValue];
int exponent = decimal._exponent; // -14, should be -1
int mantissa = decimal._mantissa[0]; // should be 999
```

Expected Results:
When `NSNumberFormatter.generatesDecimalNumbers == YES`, `-[NSNumberFormatter numberFromString:]` should produce `NSDecimalNumber` with true decimal representation. 

Actual Results:
When `NSNumberFormatter.generatesDecimalNumbers == YES`, `-[NSNumberFormatter numberFromString:]` should produces `NSDecimalNumber` with decimal representation of rewrapped number binary approximation. Produced number is affected by binary approximation error.

Version:
iOS 10.2, macOS 10.11.6

Notes:
Optionally `NSNumberFormatter.generatesDecimalNumbers` could be deprecated, and `-[NSNumberFormatter numberFromString:]` could always produce `NSDecimalNumber`.

Configuration:
iPhone 6 Plus, MacBook Pro (Retina, 13-inch, Early 2015)

Attachments:

Comments


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!