为什么在python中的一些浮点乘法中会出现那些奇怪的残差?
例如
>>> 50*1.1
55.00000000000001
但
>>> 30*1.1
33.0
原因应该在浮点数的二进制表示中的某个地方,但是这两个示例的区别在哪里?
(这个答案假设你的Python实现使用IEEE-754二进制64,这很常见。
当1.1
转换为浮点时,结果正好是 1.100000000000000088817841970012523233890533447265625,因为这是最接近的可表示值。(这个数字是 4953959590107546 • 2−52— 一个整数,最多 53 位乘以 2 的幂。
当乘以 50 时,精确的数学结果是 55.0000000000000000444089209850062616169452667236328125。这不能在二进制64中完全表示。为了使其适合 binary64 格式,它被舍入到最接近的可表示值,即 55.00000000000000710542735760100185871124267578125(即 7740561859543041 • 2−47(。
当它乘以 30 时,确切的结果是 33.000000000000000266453525910037569701671600341796875。 它也不能在二进制64中精确表示。它四舍五入到最接近的可表示值,即 33。(下一个更高的可表示值是 33.00000000000000710542735760100185871124267578125,我们可以看到......026更接近...000比...071.(
这就解释了内部结果是什么。接下来是 Python 实现如何格式化输出的问题。我不认为 Python 实现对此很严格,但很可能使用了两种方法之一:
- 实际上,该数字被转换为一定数量的十进制数字,然后删除尾随的无关紧要的零。将 55.000000000000000710542735760100185871124267578125 转换为小数点后有 16 位数字的数字,将得到 55.00000000000001,其中没有要删除的尾随零。将 33 转换为小数点后有 16 位数字的数字将得到 33.0000000000000,其中有 15 个尾随零要删除。(大概你的 Python 实现总是在小数点后至少留下一个尾随零,以清楚地区分它是一个浮点数而不是整数。
- 恰到好处的十进制数字用于唯一地区分数字与相邻的可表示值。此方法在 Java 和 JavaScript 中是必需的,但在其他编程语言中尚不常见。对于 55.000000000000000710542735760100185871124267578125,打印"55.0000000000000001"将其与相邻值 55(格式为"55.0"(和 55.0000000000000142108547152020037174224853515625(即"55.0000000000000014"(区分开来。