Swift's Decimal precision issue

随声附和 提交于 2020-01-15 10:39:28

问题


According to the docs here, Swift 3/4 Decimal type is a representation in base 10 bridged to NSDecimalNumber. However I'm having precision issues that do not reproduce when using NSDecimalNumber.

let dec24 = Decimal(integerLiteral: 24)
let dec1 = Decimal(integerLiteral: 1)
let decResult = dec1/dec24*dec24 
// prints 0.99999999999999999999999999999999999984

let dn24 = NSDecimalNumber(value: 24)
let dn1 = NSDecimalNumber(value: 1)
let dnResult = dn1.dividing(by: dn24).multiplying(by: dn24)
// prints 1

Shouldn't the Decimal struct be accurate, or am I misunderstanding something?


回答1:


NSDecimalNumber (and its overlay type Decimal) can represent

... any number that can be expressed as mantissa x 10^exponent where mantissa is a decimal integer up to 38 digits long, and exponent is an integer from –128 through 127.

So decimal fractions (with up to 38 decimal digits) can be represented exactly, but not arbitrary numbers. In particular 1/24 = 0.416666666... has infinitely many decimal digits (a repeating decimal) and cannot be represented exactly as a Decimal.

Also there is no precision difference between Decimal and NSDecimalNumber. That becomes apparent if we print the difference between the actual result and the "theoretical result":

let dec24 = Decimal(integerLiteral: 24)
let dec1 = Decimal(integerLiteral: 1)
let decResult = dec1/dec24*dec24

print(decResult - dec1)
// -0.00000000000000000000000000000000000016


let dn24 = NSDecimalNumber(value: 24)
let dn1 = NSDecimalNumber(value: 1)
let dnResult = dn1.dividing(by: dn24).multiplying(by: dn24)

print(dnResult.subtracting(dn1))
// -0.00000000000000000000000000000000000016



回答2:


The problem is simply an artefact of the way Playground formats numbers.

I typed this into Playground

import Foundation

let dn1 = Decimal(integerLiteral: 1)
let dn24 = Decimal(integerLiteral: 24)
let decResult = dn1 / dn24 * dn24
print(decResult)

let nsdn1 = NSDecimalNumber(value: 1)
let nsdn24 = NSDecimalNumber(value: 24)

let nsdecResult = nsdn1.dividing(by: nsdn24).multiplying(by: nsdn24)
print(nsdecResult)

Playground displays the number on the right hand side as 0.99999999999999999999999999999999999984 for the first calculation and 1 for the second calculation. However, both print statements printed 0.99999999999999999999999999999999999984.

Here's a picture to prove it:

Oh, and the reason why the calculation produces 0.99999999999999999999999999999999999984instead of 1 is because (as Martin R says) 1/24 cannot be represented exactly as a Decimal.



来源:https://stackoverflow.com/questions/47328916/swifts-decimal-precision-issue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!