我们都知道潮汐现象,上学的时候老师多半简单解释一句「月球引力所致」就算了,而我们也都觉得自己明白了,但是凡事就怕琢磨:如果涨潮仅仅是月球对地球万有引力的作用结果的话,那么每天同一个地点,应该仅仅在距离月球最近引力最强的时候有一次涨潮才对,但是住在海边的人都知道,同一个地点,每天会有两次涨潮,为什么?
我抛出这个问题并不是我转行搞物理学了,而是我发现很多司空见惯的问题,如果深究的话,你就会发现很多人根本就没搞懂。浮点数运算就是这样一个问题,每个人都知道浮点数运算有精度损失,但是为什么「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」:
代码语言:javascript复制 0.00011
0.01001
---------
0.01100
不多不少,答案正好是 0.4!也就是说,虽然有精度损失,但是刚刚好碰巧抵消了彼此的误差。希望大家阅读完本文之后,能够彻底搞清楚浮点数运算的相关问题,如果还有不清楚的地方,推荐阅读:IEEE 754 和 THE FLOATING-POINT GUIDE。