0

为什么「0.1+0.2!=0.3」,而「0.1+0.3==0.4」

 3 years ago
source link: https://blog.huoding.com/2019/08/23/769
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

为什么「0.1+0.2!=0.3」,而「0.1+0.3==0.4」

发表于2019-08-23

我们都知道潮汐现象,上学的时候老师多半简单解释一句「月球引力所致」就算了,而我们也都觉得自己明白了,但是凡事就怕琢磨:如果涨潮仅仅是月球对地球万有引力的作用结果的话,那么每天同一个地点,应该仅仅在距离月球最近引力最强的时候有一次涨潮才对,但是住在海边的人都知道,同一个地点,每天会有两次涨潮,为什么

我抛出这个问题并不是我转行搞物理学了,而是我发现很多司空见惯的问题,如果深究的话,你就会发现很多人根本就没搞懂。浮点数运算就是这样一个问题,每个人都知道浮点数运算有精度损失,但是为什么「0.1+0.2!=0.3」,而「0.1+0.3==0.4」:

float

除了含含糊糊的精度损失,你能给出更有营养的解释么?让我们看看到底是为什么!

首先,让我们举一个整数的例子,比如:

  • 十进制「13」:1*(10^1) + 3(10^0) = 10 + 3 = 13
  • 二进制「1101」:1*(2^3) + 1*(2^2) + 0*(2^1) + 1*(2^0) = 8 + 4 + 0 + 1 = 13

接着,让我们再举一个小数的例子,比如:

  • 十进制「0.625」:6*(10^-1) + 2*(10^-2) + 5*(10^-3) = 0.625
  • 二进制「0.101」:1*(2^-1) + 0*(2^-2) + 1*(2^-3) = 5/8 = 0.625

最重要的一点是你要明白计算机是如何表示小数的:比如二进制的「0.1111111」,无非就是十进制的「1/2 + 1/4 + 1/8 + 1/16 + 1/32 + 1/64 + 1/128」,不过细心的你可能已经发现问题了,计算机这种处理小数的方式存在精度损失的,比如一个十进制的「0.1」,换算成分数的话就是十进制的「1/10」,对比前面的结果,你会发现计算机没办法精确表示它,只能近似等于二进制的「0.00011」,也就是十进制的「1/16 + 1/32 = 3/32」,当然二进制小数点后可以多取几位,可惜结果是只能无限趋近,但永远不可能等于。

下面看看为什么「0.1 + 0.2 != 0.3」,而「0.1 + 0.3 == 0.4」。既然存在精度损失,那么「0.1 + 0.2 != 0.3」也说得过去,我们推算一下为什么「0.1 + 0.3 == 0.4」:

  • 十进制的「0.1」近似等于二进制「0.00011」
  • 十进制的「0.3」近似等于二进制「0.01001」
  • 十进制的「0.4」近似等于二进制「0.01100」

于是,十进制的「0.1 + 0.3」也就是二进制的「0.00011 + 0.01001」:

  0.00011
+ 0.01001
---------
  0.01100

不多不少,答案正好是 0.4!也就是说,虽然有精度损失,但是刚刚好碰巧抵消了彼此的误差。希望大家阅读完本文之后,能够彻底搞清楚浮点数运算的相关问题,如果还有不清楚的地方,推荐阅读:IEEE 754 和 THE FLOATING-POINT GUIDE

此条目是由老王发表在Technical分类目录的。将固定链接加入收藏夹。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK