为什么以下代码:
from decimal import Decimal
result = Decimal('0') * Decimal('0.8881783462119193534061639577')
print(result)
<返回strong>0即使返回strong>
?我已经跟踪到模块中的以下代码:
if not self or not other:
ans = _dec_from_triple(resultsign, '0', resultexp)
# Fixing in case the exponent is out of bounds
ans = ans._fix(context)
return ans
代码似乎遵循十进制算术规范,该规范没有明确建议当我们乘以零时该怎么做,引用另一个标准中的"特殊数字",也没有指定当我们将整数乘以零时该怎么做:)所以小数库做的事情是是明确指定:
- 在舍入之前,通过将操作数的系数相乘计算结果的系数。
- 结果的指数,在舍入之前,是两个操作数的指数之和。
- 结果的符号是操作数符号的异或。
问题:如果其中一个操作数为零,需要返回系数和指数(即0E-28)是什么?在调用乘法函数时我们已经知道这个系数是什么了。为什么不直接返回0呢?
Raymond Hettinger在cpython github上给出了全面的解释:
在算术运算中,算术运算规则一节告诉我们:
操作后不删除后面的零。
有一些测试用例涵盖了乘零。下面是一些来自multiply.decTest:
-- zeros, etc.
mulx021 multiply 0 0 -> 0
mulx022 multiply 0 -0 -> -0
mulx023 multiply -0 0 -> -0
mulx024 multiply -0 -0 -> 0
mulx025 multiply -0.0 -0.0 -> 0.00
mulx026 multiply -0.0 -0.0 -> 0.00
mulx027 multiply -0.0 -0.0 -> 0.00
mulx028 multiply -0.0 -0.0 -> 0.00
mulx030 multiply 5.00 1E-3 -> 0.00500
mulx031 multiply 00.00 0.000 -> 0.00000
mulx032 multiply 00.00 0E-3 -> 0.00000 -- rhs is 0
mulx033 multiply 0E-3 00.00 -> 0.00000 -- lhs is 0
mulx034 multiply -5.00 1E-3 -> -0.00500
mulx035 multiply -00.00 0.000 -> -0.00000
mulx036 multiply -00.00 0E-3 -> -0.00000 -- rhs is 0
mulx037 multiply -0E-3 00.00 -> -0.00000 -- lhs is 0
mulx038 multiply 5.00 -1E-3 -> -0.00500
mulx039 multiply 00.00 -0.000 -> -0.00000
mulx040 multiply 00.00 -0E-3 -> -0.00000 -- rhs is 0
mulx041 multiply 0E-3 -00.00 -> -0.00000 -- lhs is 0
mulx042 multiply -5.00 -1E-3 -> 0.00500
mulx043 multiply -00.00 -0.000 -> 0.00000
mulx044 multiply -00.00 -0E-3 -> 0.00000 -- rhs is 0
mulx045 multiply -0E-3 -00.00 -> 0.00000 -- lhs is 0
这个来自示例:
mulx053 multiply 0.9 -0 -> -0.0
在Summary of Arithmetic一节中,从一个较高的层次解释了动机:
该算法被设计为十进制扩展浮点运算,直接实现了人们所学习的规则学校。在给定的工作精度下,可以得到精确的非舍入结果在可能的情况下给出(例如,0.9 ÷ 10得0.09,而不是。0.089999996),并且在大多数操作中都正确地保留了后面的零(1.23 + 1.27得到2.50,而不是2.5)。结果会怎样?超过工作精度,适用浮点规则。
更多细节见FAQ一节为什么后面的小数零很重要?