我知道大多数小数都没有精确的浮点表示(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')
发布于 2016-09-21 22:30:12
简单的答案是由于量化(舍入)误差导致的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稍大一些。
Python3的float repr
被设计为可往返的,也就是说,显示的值应该可以精确地转换为原始值(对于所有floats f
,都是float(repr(f)) == f
)。因此,它不能以完全相同的方式显示0.3
和0.1*3
,否则两个不同的数字在往返后可能会相同。因此,Python3的repr
引擎选择显示一个带有轻微明显错误的代码。
发布于 2016-09-21 22:26:14
repr
(和Python3中的str
)将根据需要输出尽可能多的数字,以使值明确。在这种情况下,乘法3*0.1
的结果不是最接近0.3的值(十六进制的0x1.33333333333p-2),它实际上比0.3高一个LSB (0x1.33333333334p-2),所以它需要更多的数字来区分0.3。
另一方面,乘法4*0.1
得到的值最接近0.4 (十六进制的0x1.999999999999ap-2),因此它不需要任何额外的数字。
您可以很容易地验证这一点:
>>> 3*0.1 == 0.3
False
>>> 4*0.1 == 0.4
True
我使用上面的十六进制表示法,因为它很好和紧凑,并显示了两个值之间的位差异。你可以使用例如(3*0.1).hex()
自己来做这件事。如果你更愿意看到他们所有的小数荣耀,这是你的:
>>> Decimal(3*0.1)
Decimal('0.3000000000000000444089209850062616169452667236328125')
>>> Decimal(0.3)
Decimal('0.299999999999999988897769753748434595763683319091796875')
>>> Decimal(4*0.1)
Decimal('0.40000000000000002220446049250313080847263336181640625')
>>> Decimal(0.4)
Decimal('0.40000000000000002220446049250313080847263336181640625')
https://stackoverflow.com/questions/39618943
复制相似问题