Swift的小数精度问题

Swift's Decimal precision issue

根据文档 here,Swift 3/4 十进制类型是以 10 为基数桥接到 NSDecimalNumber 的一种表示形式。但是,我遇到了使用 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

Decimal 结构不应该是准确的,还是我误会了什么?

NSDecimalNumber(及其叠加类型 Decimalcan 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.

所以小数(最多38位小数)可以表示 确切地说,但不是任意数字。特别是 1/24 = 0.416666666... 有无限多个小数位(a repeating decimal)并且不能 完全表示为 Decimal.

DecimalNSDecimalNumber之间也没有精度差异。如果我们打印差异,那将变得很明显 实际结果与 "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

问题只是 Playground 格式化数字的方式造成的。

我在 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 在第一次计算时将右侧的数字显示为 0.99999999999999999999999999999999999984,在第二次计算时显示为 1。但是,两个打印语句都打印了 0.99999999999999999999999999999999999984.

这里有一张图片可以证明:

哦,计算产生 0.99999999999999999999999999999999999984 而不是 1 的原因是因为(正如 Martin R 所说)1/24 不能完全表示为 Decimal