Reputation: 1957
I test the NumberFormatter to get the number from priceWithCurrency.
If price bigger than $70 NumberFormatter converted the wrong number.
lazy var currencyFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencyCode = "USD"
return formatter
}()
let price = "$71.9"
currencyFormatter.number(price)
//71.90000000000001
Upvotes: 0
Views: 385
Reputation: 2737
If you do the same thing with 71.8, it will work. This is just because 71.9 can't be represented precisely in floating point numbers ( due to the finitude of bits )
Use integers ( price in cents ) or decimal numbers is the only issue to deal correctly with prices. Check the Decimal
and NSDecimalNumber
classes to see what you can do with this.
It is specially recommended if you do some computations on prices, ( If you pay 10$ with two friends in cash, two will pay 3.33, and one will pay 3.34 - so depending of your context, divide by 3 might not be enough)
let number = currencyFormatter.number(from: price) ?? NSNumber(value: 0)
var value = number.decimalValue
var roundedValue: Decimal = 0
// Right way: ( choose your precisions - 3 decimals here):
NSDecimalRound(&roundedValue, &value, 3, .bankers)
print(number)
print(value)
print(roundedValue)
71.90000000000001
71.90000000000001
71.9
If you just need to log your prices, simply use Int(price*100)/100
It will do the job
If you need more... Good luck :)
Edit
Following the excellent remark of @Nicholas Rees, I add this variation:
currencyFormatter.generatesDecimalNumbers = true
let number = (currencyFormatter.number(from: price) as? NSDecimalNumber) ?? NSDecimalNumber(value: 0)
var value = number.decimalValue
var roundedValue: Decimal = 0
// Right way: ( choose your precisions - 3 decimals here):
NSDecimalRound(&roundedValue, &value, 3, .bankers)
print(number)
print(value)
print(roundedValue)
There, the result in the same when logged, but I suppose the internal format of the 'value' is correct
Another approach is to remove currency and create decimal from string:
print(Decimal(string: "71.9") ?? 0)
71.9
Upvotes: 3