我知道大多数小数都没有确切的浮点表示形式(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
方法查看数字的内部表示形式(基本上是确切的二进制浮点值,而不是以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-换句话说,二进制浮点数中的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的float
repr
设计为可双向访问的,也就是说,显示的值应可精确转换为原始值(所有floats float(repr(f)) == f
的f
)。因此,它不能以完全相同的方式显示0.3
和0.1*3
,否则两个不同的数字在往返后将以相同的方式结束。因此,Python 3的repr
引擎选择显示一个带有明显错误的代码。