首先,Java中的小数使用float和double表示(附下图),小数属于浮点型(默认为double)。

类型字节取值范围
float(单精度)4字节(32位)-3.403E38-~3.403E38

double(双精度)
8字节(64位)-1.798E308·1.798E308

计算机之所以叫"计算"机就是因为发明它主要是用来计算的,"计算"当然是它的特长,在大家的印象中,计算一定是非常准确的。但实际上,在一些非常基本的小数运算中,计算的结果是不精确的。

比如:

 这个结果计算器算应该是0.01,但实际上,在计算机中输出的却是0.010000000000000002。看上去这么简单的运算,计算机怎么会出错呢?

实际上,不是运算本身会出错,而是计算机根本就不能精确的表示很多数,比如0.1这个数。我们要知道计算机是用二进制存储和运算数字的,这个二进制不能精确表示0.1,它只能表示一个非常接近0.1但又不等于0.1的一个数。数字都不能精确表示,在不精确数字上的运算结果不精确也就不足为奇了。

我们再来了解一下小数在Java中是如何存储的:

(1)一个浮点数有3部分组成:符号位,指数位和尾数位。

(2)float类型符号位占1位,指数部分占用8bit(1个字节)的二进制数,可表示数值范围为0-255,尾数占23位(因为规格化表示,小数点左边的就是最高位一定为1,最高位省去不存储,在存储中占23bit,实际有24位精度)

(3)double类型符号位占1位,指数部分占11位,尾数占52位(因为规格化表示,小数点左边一定为1,所以实际有53位精度)
这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。

最后我们看一下十进制与二进制的相互转换:

十进制整数转换为二进制整数采用"除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.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.2”接着计算。
0.8*2=1.6……1——整数部分为“1”。整数部分“1”清零后为“0”,用“0.2”接着计算。
……
……
所以,得到的整数依次是:“0”,“0”,“0”,“1”,“1”,“0”,“0”,“1”,“1”,“0”,“0”,“1”……
由此,大家肯定能看出来,整数部分出现了无限循环。

我们发现0.1的二进制表示中出现了无限循环的情况,这种情况计算机就没办法用二进制精确的表示了。

综上所述,就可以验证"大部分浮点数在计算机中是不精确的"这个结论了。

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信