原创

0.1+0.2还要用电脑算,程序员都是小学没毕业?

如果你问一个小学生 0.1+0.2等于多少,我相信他会毫不犹豫地告诉你等于0.3。但是如果你去问一个程序员,他就会很谨慎地告诉你,稍等一下,然后飞快地在电脑上敲下几行代码:

public class Demo {
public static void main(String[] args) {
System.out.println(0.1 + 0.2);
}
}

这时候你心里肯定在说:”这哥们小学没毕业?这种问题还要用电脑算?“ 就在你即将不屑地转身离开时,电脑上出现了计算结果:

0.30000000000000004

”什么鬼?我读书少你别骗我,一定是电脑坏了!“

OK,电脑没有坏,那强大的电脑和聪明绝顶的程序员为什么比不上一个小学生呢?我们来一起分析一下:

首先我们来复习一下义务教育阶段学习的一个重要知识点:科学计数法

把一个数表示成a与10的n次幂相乘的形式(1≤|a|<10,n为整数)

我们都知道计算机中的数据都是以二进制形式来表示的,在内存中有两种存储方式:

float(单精度)
1位符号位
8位指数位
23位尾数位
double(双精度)
1位符号位
11位指数位
52位尾数位

首先将十进制0.1转换为二进制,十进制小数转二进制一般采用乘2取整法:

0.1 x 2 = 0.2  整数部分为 0 小数部分继续 x 2

0.2 x 2 = 0.4  整数部分为 0 小数部分继续 x 2

0.4 x 2 = 0.8  整数部分为 0 小数部分继续 x 2

0.8 x 2 = 1.6  整数部分为 1 小数部分继续 x 2

0.6 x 2 = 1.2  整数部分为 1 小数部分继续 x 2

0.2 x 2= 0.4   整数部分为 0 小数部分继续 x 2

...

重复计算得到64位二进制小数:

0.0001100110011001100110011001100110011001100110011001100110011001

以二进制科学计数法来表示,也就是a与2的n次幂相乘的形式(1≤|a|<2,n为整数),a就是尾数,n就是指数,即
1. 100110011001100110011001100110011001100110011001100110011001 * 2 ^ -4

先来处理尾数,JAVA浮点数默认为双精度,使用52位存储尾数,由于二进制的科学计数法整数位永远都是1,因此可以直接不存储,截取小数部分前52位为尾数位(0舍1入)

1001100110011001100110011001100110011001100110011010 

再来处理指数,由于指数位为11位,因此指数偏移值为2 ^ 10 -1 = 1023, 指数位实际值 x - 1023 = -4, 得到x为1019,转为二进制1111111011, 高位补0,满11位,得01111111011

正数符号位为0,得到0.1的双精度二进制表示为:

0   01111111011    (1.)1001100110011001100110011001100110011001100110011010

同理,得到0.2的双精度二进制表示为:

0   01111111100    (1.)1001100110011001100110011001100110011001100110011010

在相加之前将0.1的指数调整和0.2的指数相同(即-4调为-3),同时将尾数右移一位得到(0.)11001100110011001100110011001100110011001100110011010,根据0舍1入原则将最后一位0舍去得到

0  01111111100     (0.)1100110011001100110011001100110011001100110011001101 

现在可以将尾数相加了:

   0.1100110011001100110011001100110011001100110011001101
+ 1.1001100110011001100110011001100110011001100110011010
————————————————————————————————————————————————————————
10.0110011001100110011001100110011001100110011001100111   

尾数计算结果规格化处理,向右移一位(最后一位1舍去),同时将指数+1得到双精度计算结果

0   01111111101 (1.) 0011001100110011001100110011001100110011001100110011(1)

由于右规了一位1,因此需要进行+1预算

   0011001100110011001100110011001100110011001100110011
+ 0000000000000000000000000000000000000000000000000001
————————————————————————————————————————————————————————
   0011001100110011001100110011001100110011001100110100

最终得到的结果为:

0 01111111101 0011001100110011001100110011001100110011001100110100 

转为10进制为:

0.30000000000000004440892098500626

这就是0.1+0.2不等于0.3的原因所在。

正文到此结束