python - 为什么 4*0.1 的浮点值在 Python 3 中看起来不错,但 3*0.1 不

我知道大多数小数没有精确的浮点表示 (Is floating point math broken?)。

但我不明白为什么 4*0.1 可以很好地打印为 0.4,但 3*0.1 不是,当 这两个值实际上都有丑陋的十进制表示:

>>> 3*0.1
0.30000000000000004
>>> 4*0.1
0.4
>>> from decimal import Decimal
>>> Decimal(3*0.1)
Decimal('0.3000000000000000444089209850062616169452667236328125')
>>> Decimal(4*0.1)
Decimal('0.40000000000000002220446049250313080847263336181640625')

最佳答案

简单的答案是因为 3*0.1 != 0.3 由于量化(舍入)误差(而 4*0.1 == 0.4 因为乘以 2 的幂通常是“精确”操作)。 Python 试图找到可以四舍五入到所需值的最短字符串,因此它可以将 4*0.1 显示为 0.4,因为它们是相等的,但它不能将 3*0.1 显示为 0.3 因为它们不相等。

您可以使用 Python 中的 .hex 方法查看数字的内部表示(基本上是 exact 二进制浮点值,而不是 base-10近似)。这有助于解释幕后发生的事情。

>>> (0.1).hex()
'0x1.999999999999ap-4'
>>> (0.3).hex()
'0x1.3333333333333p-2'
>>> (0.1*3).hex()
'0x1.3333333333334p-2'
>>> (0.4).hex()
'0x1.999999999999ap-2'
>>> (0.1*4).hex()
'0x1.999999999999ap-2'

0.1 是 0x1.999999999999a 乘以 2^-4。末尾的“a”表示数字 10 - 换句话说,二进制 float 中的 0.1非常略大于“精确”值 0.1(因为最终的 0x0.99 向上取整到 0x0.a)。当您将此乘以 4(2 的幂)时,指数会向上移动(从 2^-4 到 2^-2),但数字不会改变,因此 4*0.1 == 0.4

但是,当您乘以 3 时,0x0.99 和 0x0.a0 (0x0.07) 之间的微小差异会放大为 0x0.15 错误,在最后一个位置显示为一位数错误。这会导致 0.1*3 比 0.3 的舍入值非常轻微大。

Python 3 的浮点 repr 被设计为 round-trippable,也就是说,显示的值应该完全可以转换为原始值(float(repr (f)) == f 对于所有 float f)。因此,它不能以完全相同的方式显示0.30.1*3,否则两个不同的数字会在往返后最终相同.因此,Python 3 的 repr 引擎选择显示一个带有轻微明显错误的文件。

关于python - 为什么 4*0.1 的浮点值在 Python 3 中看起来不错,但 3*0.1 不好看?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39618943/

相关文章:

linux - 如何为安装程序编写 "yes"响应脚本?

linux - 如何将命令存储在 shell 脚本的变量中?

python - 在 Python 中注释函数的正确方法是什么?

linux - 如何在命令行中合并图像?

linux - 如何在 Bash 中解析 CSV 文件?

python - 如何在 Python Django 中运行单元测试时禁用日志记录?

linux - 有没有办法改变 Unix 中另一个进程的环境变量?

python - 如何模拟在 with 语句中使用的 open (使用 Python 中的 Mock

Python - doctest 与 unittest

c - Linux:是否有超时的套接字读取或接收?