Converting Double to NSNumber in Swift Loses Accuracy

joelrorseth picture joelrorseth · Aug 24, 2016 · Viewed 15.5k times · Source

For some reason, certain Doubles in my Swift app are giving me trouble when converting to NSNumber, while some are not. My app needs to convert doubles with 2 decimal places (prices) to NSNumbers so they can be stored and retrieved using Core Data. For example, a few particular prices such as 79.99 would evaluate to 99.98999999999999 unless specifically formatted using NSNumber's doubleValue method.

Here selectedWarranty.price = 79.99 as shown in debugger

// item.price: NSNumber?       
// selectedWarranty.price: Double?  

item.price = NSNumber(double: selectedWarranty.price!)

I programmed some print statements to show how the conversion works out

Original double: 79.99
Converted to NSNumber: 79.98999999999999
.doubleValue Representation: 79.99

Can somebody explain if there is a reason why the initializer cannot surely keep 2 decimal places for every number? I would really like to store the prices in Core Data like they should be. Formatting every time it is displayed doesn't sound very convenient.

UPDATE: Converted Core Data object to type NSDecimalNumber through data model, 79.99 and 99.99 no longer a problem, but now more manageable issue with different numbers...

Original double: 39.99
Converted to NSDecimalNumber: 39.99000000000001024

Answer

Alexander picture Alexander · Aug 24, 2016

Firstly, you're confusing some terms. 79.98999999999999 is higher precision than 79.99 (it has a longer decimal expansion), but lower accuracy (it deviates from the true value).

Secondly, NSNumber does not store neither 79.99 nor 79.98999999999999. It stores the magnitude of the value according to the IEEE 754 standard. What you're seeing is likely the consequence of the printing logic that's applied to convert that magnitude into a human readable number. In any case, you should not be relying on Float or Double to store values with a fixed precision. By their very nature, they sacrifice precision in order to gain a longer range of representable values.

You would be much better off representing prices as an Int of cents, or as an NSDecimalNumber.

Please refer to Why not use Double or Float to represent currency?