Swift的十进制精度问题

3
根据此处的文档,Swift 3/4中的Decimal类型是基于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结构体应该是准确的,或者我有什么误解吗?


你确认了打印时数字显示的问题不是导致错误的原因吗? - Itai Ferber
2个回答

7

NSDecimalNumber(以及其覆盖类型Decimal可以表示任何可以表示为mantissa x 10^exponent的数字,其中mantissa是一个最多38位十进制整数,而exponent是从-128到127的整数。

因此,小数(最多有38个小数位)可以被精确地表示,但不是任意数字。特别地,1/24 = 0.416666666...有无限多个小数位(一个循环小数),不能作为Decimal精确表示。

此外,DecimalNSDecimalNumber之间没有精度差异。如果我们打印实际结果与“理论结果”之间的差异,则会变得明显:

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

1
我认为这里的问题是为什么使用Decimal时得到的看起来像是0.9999999...,而使用NSDecimalNumber却得到了1。我相信这只是数字打印方式上的差异。 - Itai Ferber
2
@ItaiFerber:OP问道“Decimal结构体不应该是准确的吗,...?”这就是我试图回答的问题。 - Martin R
是的,那就是这个问题(和误解)。它无法准确地表示一个重复的小数。 - Rafael Nobre

4
问题只是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

这里是一张图片来证明:

enter image description here

哦,计算结果为0.99999999999999999999999999999999999984而不是1的原因是因为(正如Martin R所说),1/24不能被准确地表示为Decimal


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接