关于Java中的浮点数
浮点数在内存中是如何存储的?
我们知道,任何数据在计算机内存中都是用‘0\1’来存储的,浮点数亦是如此。因此十进制浮点数在存储时必定会转换为二进制的浮点数。
浮点数的进制转换
主要看看十进制转二进制,整数部分和小数部分分开处理
-
整数部分:整数除以2,得到一个商和余数,得到的商继续除以2并得到一个商和一个余数,继续除以2操作直至商为0,上述操作得到一系列余数,从最后一个余数开始直至第一个余数,这一系列0\1即为转换后的二进制数。
-
小数部分:乘以2,然后取出整数部分,将剩下的小数部分继续乘以2,然后再取整数部分,一直取到小数部分为零为止。如果永远不为零,则按要求保留足够位数的小数,最后一位做0舍1入。将取出的整数顺序排列。
从以上转换过程可以看出,并不是任何一个十进制小数都可以用二进制精确表示出来。一个在0到1之间的小数P可用如下形式表示:
从这个式子中我们也可看出二进制表示出的小数是分段的,这也是为什么在Java中浮点数很多时候并不是十分精确的表示十进制小数的根本原因。
float在内存中的表示
float在Java中是4字节(32位),取值范围大约-3.4E+38F~3.4E
+38F。
为什么取值范围是这个呢?
二进制在内存中使用二进制的科学计数法来存储,因此分为阶码(即指数)和底数,由于也有正负之分,所以还有一位符号位。
最高位为符号位,接着8位阶码,剩下23位为底数。
值得注意的是,无论底数是什么值,我们都可以进行移位操作,使得底数=1.xxxxxx,这类似于十进制中的任意一个数转换为用科学计数法表示时进行的移位操作。由于底数前的一个1是固定不变的,故将其省略(为了表示更大的范围)。也就是说实际上底数是24位。
底数可以表示的最大值为1.1111111111111111111111111(共24个1),转换为十进制的值约为1.999999(接近于2)。
底数的最小值为1.00000000000000(23个0),转换为十进制为1。
故可知,底数取值在1~2之间。
阶码在这里用移码表示,采用偏移值为127的移码,为什么要这样做?浮点数的阶码中,全0和全1被保留作特殊情况,所以实际只有254个值可用。
阶码必定是有负值的,那么如何最优雅地表示负值呢?为了让计算机很容易比较阶码大小(以此来比较浮点数的大小),同时人也可以很轻松地辨别大小,因为移码相当于实际值在数轴上平移了一定位数。
我觉得应该是为了平衡精度与范围,并且让表示的范围尽可能大。
阶码用移码表示后,8位二进制从1254以此表示为-126127。不仅去除了全0和全1的情况,还能让人一眼看出两个阶码的大小。
知道阶码和底数的范围后,就可以得出float的取值范围:
转化为十进制为
-3.4*10^38~-1.2*10^(-38)
与1.2*10^(-38)~3.4*10^38