首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么4*0.1的浮点值在Python3中看起来很好,而3*0.1就不好了?

为什么4*0.1的浮点值在Python3中看起来很好,而3*0.1就不好了?
EN

Stack Overflow用户
提问于 2016-09-21 22:07:21
回答 2查看 14.6K关注 0票数 161

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

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

代码语言:javascript
复制
>>> 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')
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 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的近似值)。这可以帮助解释引擎盖下发生的事情。

代码语言:javascript
复制
>>> (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.30.1*3,否则两个不同的数字在往返后可能会相同。因此,Python3的repr引擎选择显示一个带有轻微明显错误的代码。

票数 306
EN

Stack Overflow用户

发布于 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),因此它不需要任何额外的数字。

您可以很容易地验证这一点:

代码语言:javascript
复制
>>> 3*0.1 == 0.3
False
>>> 4*0.1 == 0.4
True

我使用上面的十六进制表示法,因为它很好和紧凑,并显示了两个值之间的位差异。你可以使用例如(3*0.1).hex()自己来做这件事。如果你更愿意看到他们所有的小数荣耀,这是你的:

代码语言:javascript
复制
>>> 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')
票数 76
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39618943

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档