leisurelicht / wtfpython-cn

wtfpython的中文翻译/施工结束/ 能力有限,欢迎帮我改进翻译

Home Page:https://github.com/leisurelicht/wtfpython-cn/blob/master/CONTRIBUTING.md

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

>>> 0.4-0.3 结果为啥是 0.10000000000000003

Cuick opened this issue · comments

image

有没有大佬解释一下这是啥原因呢?

知道了

虽然 @Cuick 已经找到了答案,写一下自己的理解:

[ins] In [14]: from decimal import *                                                                                                    

[ins] In [15]: getcontext().prec                                                                                                        
Out[15]: 28

[ins] In [16]: Decimal.from_float(0.3)                                                                                                  
Out[16]: Decimal('0.299999999999999988897769753748434595763683319091796875')

[ins] In [17]: Decimal.from_float(0.1 + 0.2)                                                                                            
Out[17]: Decimal('0.3000000000000000444089209850062616169452667236328125')


[ins] In [18]: 0.3                                                                                                                      
Out[18]: 0.3

[ins] In [19]: 0.1 + 0.2                                                                                                                
Out[19]: 0.30000000000000004

上面代码直接展示了0.1+0.20.3不是同一个东西,这不只是Python中的情况,可以说绝大多数编程语言的浮点数在这个问题上都是一致的。

  • [16]的输出是直接把0.3转换成二进制表示对应的十进制的值
  • [17]的输出是分别把0.10.2转换成二进制表示相加之后对应的十进制的值
  • [18]的输出对应[16]
  • [19]的输出对应[17],这个很直接二者都有0.30000000000000004

也就是说在Python解释器中直接输入0.3,其实际的二进制对应的值是0.3000000000000000444089209850062616169452667236328125,然后解释器print的时候肯定不是全部输出而是要round一下,那输出几位呢?原则就是**在没有引起混乱的情况下,输出的位数就尽量短`。

为什么这么说呢?

因为不是所有的浮点数都有二进制的精确表示,并且浮点数的二进制位数有限制,所以会存在多个浮点数对应一个二进制的情况(实际就是一个浮点数二进制对应了实数域的一个连续区间,该区间内的浮点数都会映射到这个二进制表示),比如:

[ins] In [23]: Decimal.from_float(0.30000000000000003) # 和0.1+0.2对应的一样,也就是输入和输出不一样                                                                                 
Out[23]: Decimal('0.3000000000000000444089209850062616169452667236328125')

上面那个不引起混乱的规则就是将输出的值反过来代入Decimal.from_float之后还必须是0.3000000000000000444089209850062616169452667236328125而不能是其他的值(还能映射回那个二进制)。而0.3000000000000000444089209850062616169452667236328125,不引起混乱的最短打印值(输出位数尽量短规则)就是round到0.30000000000000004位数再少就变成了0.3000000000000000也就是0.3,这个就换成二进制就是[16]的输出了。

ref

一个浮点数到底是怎么被转换为字符串输出?一个浮点数不精确,那么其输出的值是怎么被确定的呢? - 大宽宽的回答 - 知乎

二进制表示浮点数会有截断误差 就是精度的问题 是正常的
问题是 如果我们想比较 0.1+0.2 之后的数值是否和0.3相等
也就是说从程序员的角度看 他们应该是相等的 输出是true
但是如果从计算机的角度看他们可能不等 那怎么解决呢?