一、小数是如何在计算机中存储的?
Java 有四种整数类型,int,byte,short,long,小数有两种类型,float,double,那小数是怎么表示的呢?
78.375 的整数部分:
()
小数部分:
所以,78.375的二进制形式就是1001110.011
然后,使用二进制科学记数法,有
()()
注意,转换后用二进制科学记数法表示的这个数,有底有指数有小数部分,这个就叫做浮点数
二、浮点数在计算机中的存储
按照 IEEE 制定的浮点数表示法来进行 float,double 运算。这种结构是一种科学计数法:用符号、指数和尾数来表示。指数可正可负,所以,IEEE 规定,此处算出的次方必须减去 127 才是真正的指数。底数定为 2,即把一个浮点数表示为尾数乘以 2 的指数次方再添上符号。
在计算机中,保存这个数使用的是浮点表示法,分为三大部分:
第一部分用来存储符号位(sign),用来区分正负数,这里是 0,表示正数
第二部分用来存储指数(exponent),这里的指数是十进制的 6
第三部分用来存储小数(fraction),这里的小数部分是 001110011
float位格式
double位格式
对应格式如下:
类型
符号位
指数
尾数
长度
float
1
8
23
32
double
1
11
52
64
以 float 为例:
格式:
SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM
S表示浮点数正负
E表示指数加上 127 的值后得到的二进制数据
M表示尾数,最高位固定为 1
举例:
78.375 在内存中的存储为:
上面我们已经算出,二进制表示为:1.001110011*26
符号:因为是正数,所以第一位为 0
指数:一共 6 位,所以指数为 6+127,所以为 133,二进制为:1000 0101
底数:因为小数点前必为 1,所以 IEEE 规定只记录小数点后的就好。所以,此处的底数为:001110011,不足部分补 0
所以内存中 78.385 表示为:
0100 0010 1001 1100 1100 0000 0000 0000
三、浮点型为啥不精确?
从图中可以看到,对于二进制小数,小数点右边能表达的值是 1/2, 1/4, 1/8, 1/16, 1/32, 1/64, 1/128 … 1/(2^n)
现在问题来了, 计算机只能用这些个 1/(2^n) 之和来表达十进制的小数。
按照上面说的 float 的格式,我们来表示一下十进制的 0.2 呢
0.01 = 1/4 = 0.25 ,太大
0.001 =1/8 = 0.125 , 又太小
0.0011 = 1/8 + 1/16 = 0.1875 , 逼近 0.2 了
0.00111 = 1/8 + 1/16 + 1/32 = 0.21875 , 又大了
0.001101 = 1/8+ 1/16 + 1/64 = 0.203125 还是大
0.0011001 = 1/8 + 1/16 + 1/128 = 0.1953125 这结果不错
0.00110011 = 1/8+1/16+1/128+1/256 = 0.19921875
精度(有效数字)主要看尾数位:
float 的尾数位是 23bit,对应 7~8 位十进制数,所以有效数字有的编译器是 7 位,也有的是 8 位
float 为 4 个字节,表示为:一个符号位,8 个指数位,23 个尾数,所以有效位数由尾数来决定,即 2^23=8388608 为 7 位,指数的范围是 2^(-127~127)
参考资料
[1]
https://blog.csdn.net/tianmd_Eric/article/details/79729827: https://blog.csdn.net/tianmd_Eric/article/details/79729827
[2]
https://www.cnblogs.com/Java-Script/p/11123897.html: https://www.cnblogs.com/Java-Script/p/11123897.html
[3]
https://blog.csdn.net/zhuxuyue/article/details/90293299: https://blog.csdn.net/zhuxuyue/article/details/90293299