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
(及其叠加类型 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.
所以小数(最多38位小数)可以表示
确切地说,但不是任意数字。特别是 1/24 = 0.416666666...
有无限多个小数位(a repeating decimal)并且不能
完全表示为 Decimal
.
Decimal
和NSDecimalNumber
之间也没有精度差异。如果我们打印差异,那将变得很明显
实际结果与 "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
。
根据文档 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
(及其叠加类型 Decimal
)can represent
... any number that can be expressed as
mantissa x 10^exponent
wheremantissa
is a decimal integer up to 38 digits long, andexponent
is an integer from –128 through 127.
所以小数(最多38位小数)可以表示
确切地说,但不是任意数字。特别是 1/24 = 0.416666666...
有无限多个小数位(a repeating decimal)并且不能
完全表示为 Decimal
.
Decimal
和NSDecimalNumber
之间也没有精度差异。如果我们打印差异,那将变得很明显
实际结果与 "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
。