我在计算70000.0*5.65500*18.0/36000.0
时遇到了一个金融应用问题,并将结果与另一个数字进行比较。
准确结果为197.925
当使用Decimal时,结果取决于操作顺序:
from decimal import Decimal
from fractions import Fraction
Decimal('70000.0')*Decimal('5.65500')*Decimal('18.0')/Decimal('36000.0')
The result is Decimal('197.925000')
Decimal('70000.0')*Decimal('5.65500')/Decimal('36000.0')*Decimal('18.0')
The result is Decimal('197.9249999999999999999999999')
当使用Decimal + Fraction时,结果仍然不准确:
Decimal('70000.0')*Decimal('5.65500')*Decimal(float(Fraction(18, 36000)))
The result is Decimal('197.9250000000000041201417278')
当使用本机浮点数时,操作顺序不影响结果,但结果仍然不准确:
Decimal(70000.0*5.65500*18.0/36000.0)
The result is Decimal('197.92500000000001136868377216160297393798828125')
Decimal(70000.0/36000.0*5.65500*18.0)
The result is Decimal('197.92500000000001136868377216160297393798828125')
将Decimal(1.0/36000.0)
或Decimal(5.655/36000.0)
作为乘法器,其顺序几乎不影响结果,但结果仍然不准确:
Decimal('70000.0')*Decimal('5.65500')*Decimal('18.0')*Decimal(1.0/36000.0)
The result is Decimal('197.9250000000000094849096025')
Decimal('70000.0')*Decimal('5.65500')*Decimal(1.0/36000.0)*Decimal('18.0')
The result is Decimal('197.9250000000000094849096026')
Decimal('70000.0')*Decimal(5.655/36000.0)*Decimal('18.0')
The result is Decimal('197.9250000000000182364540136')
Decimal('70000.0')*Decimal('18.0')*Decimal(5.655/36000.0)
The result is Decimal('197.9250000000000182364540136')
如果没有办法达到绝对精确,容错可能是一种出路:比较容错内的两个数。
本机浮点数的精度为1E-14
Decimal(70000.0/36000.0*5.65500*18.0) - Decimal('197.925000')
The result is Decimal('1.136868377216160297393798828E-14')
默认设置的十进制精度为1E-25
Decimal('70000.0')*Decimal('5.65500')/Decimal('36000.0')*Decimal('18.0') - Decimal('197.925000')
The result is Decimal('-1E-25')
Decimal的精度可由用户
设置。import decimal as decimal
from decimal import Decimal, Context
decimal.setcontext(Context(prec=60))
Decimal('70000.0')*Decimal('5.65500')/Decimal('36000.0')*Decimal('18.0')
The result is Decimal('197.924999999999999999999999999999999999999999999999999999999')
Decimal('70000.0')*Decimal('5.65500')*Decimal('18.0')/Decimal('36000.0')
The result is Decimal('197.925000')
Decimal(70000.0/36000.0*5.65500*18.0) - Decimal('197.925000')
The result is Decimal('1.136868377216160297393798828125E-14')
Decimal('70000.0')*Decimal('5.65500')/Decimal('36000.0')*Decimal('18.0') - Decimal('197.925000')
The result is Decimal('-1E-57')
在金融应用中,为了保证绝对安全,是否推荐容错?默认的十进制容错精度为1E-20是否足够?
就像一般的浮点运算一样,Python的Decimal也不是精确的,并且会出现舍入错误。与float
相比,Decimal的关键区别在于它使用以10为基数的表示,因此像"0.1"这样的值是完全可表示的
在第一个例子中,
Decimal('70000.0')*Decimal('5.65500')/Decimal('36000.0')*Decimal('18.0')
The result is Decimal('197.9249999999999999999999999')
在此计算中,Decimal使用其默认的28位精度。请注意,输出结果有28位有效数字,并且是正确的,直到最低的数字相差1。换句话说,计算在最后一个位置有1个单位的舍入误差。(ULP)。典型的情况是,涉及少量浮点操作的表达式会产生一些带有舍入错误的ulp。
我看到你已经尝试了增加Decimal的精度。这减少了舍入误差的大小,但是,仍然存在舍入误差。
如果您不希望在打印结果的末尾看到&;9&;s,则使用round(result, 12)
将底部的几位数字四舍五入。
或者,如果精确的计算是必须的,我可以理解为一个金融应用程序,那么不要使用十进制或其他浮点表示。将计算重新表述为带有分数的算术:
Fraction(70000) * Fraction(5655, 1000) * Fraction(18) / Fraction(36000)
这恰好产生了正确的答案,7917/40 = 197.925。
请参阅Python文档浮点运算:问题和限制。