首先,Java中的小数使用float和double表示(如下),小数属于浮点型(默认为double)。
类型 字节 取值范围
float(单精度) 4字节(32位) -3.403E38-~3.403E38
double(双精度) 8字节(64位) -1.798E308·1.798E308
我们都认为计算机的计算一定是非常准确的。但实际上,在一些小数运算中,计算的结果并不不精确。
比如:
这个结果我们计算应该是0.3,但实际上,在计算机中输出的却是0.30000000000000004。
由上我们不难看出,有的数字计算机并不能很精确地表示,比如0.1这个数。我们要知道计算机是用二进制存储和运算数字的,这个二进制不能精确表示0.1,它只能表示一个非常接近0.1但又不等于0.1的一个数。原有数字不能精确表示,其运算结果不精确也就不足为奇。
我们再来了解一下小数在Java中是如何存储的:
(1)一个浮点数有3部分组成:符号位,指数位和尾数位。
(2)float类型符号位占1位,指数部分占用8位的二进制数,尾数占23位。其取值约等于-2^127~2^127
(3)double类型符号位占1位,指数部分占11位,尾数占52位。其取值约等于-2^1023~2^1023
这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。
最后我们看一下十进制与二进制的相互转换:
十进制整数转换为二进制整数采用"除2取余,逆序排列"法
具体做法是:
(1)用2整除十进制整数,可以得到一个商和余数;
(2 )再用2去除商,又会得到一个商和余数,如此进行,直到商为0时为止;
(3) 然后把得到的余数从下往上,依次排列起来。
那么,十进制小数转换成二进制小数,又该如何计算呢?
十进制小数转换成二进制小数采用"乘2取整,顺序排列"法。
具体做法是:
(1)用2乘十进制小数,可以得到积
(2)将积的整数部分取出,再用2乘余下的小数部分,又得到一个积
(3)再将积的整数部分取出,如此进行,直到积中的小数部分为零,此时0或1为二进制的最后一位。或者达到所要求的精度为止。
下面我们举个例子:0.1(十进制)转换为2进制:
计算过程:
0.1*2=0.2……0——整数部分为“0”。整数部分“0”清零后为“0”,用“0.2”接着计算。
0.2*2=0.4……0——整数部分为“0”。整数部分“0”清零后为“0”,用“0.4”接着计算。
0.4*2=0.8……0——整数部分为“0”。整数部分“0”清零后为“0”,用“0.8”接着计算。
0.8*2=1.6……1——整数部分为“1”。整数部分“1”清零后为“0”,用“0.6”接着计算。
0.6*2=1.2……1——整数部分为“1”。整数部分“1”清零后为“0”,用“0.2”接着计算。
0.2*2=0.4……0——整数部分为“0”。整数部分“0”清零后为“0”,用“0.4”接着计算。
0.4*2=0.8……0——整数部分为“0”。整数部分“0”清零后为“0”,用“0.8”接着计算。
开始循环……
所以,得到的整数依次是:“0”,“0”,“0”,“1”,“1”,“0”,“0”,“1”,“1”,“0”,“0”,“1”……
由此,大家肯定能看出来,小数部分出现了无限循环。我们发现0.1的二进制表示中出现了无限循环的情况,这种情况计算机就没办法用二进制精确的表示了。
综上所述,就可以验证"大部分浮点数在计算机中是不精确的"这个结论了。