要想解决这个问题,我们需要先来了解一下float在内存中的存储
一、浮点型在内存中的存储
>>Java中float在内存中的存储占4个字节,float的32位二进制结构如下:
31 | 30 | 29-23 | 22-0 |
实数符号位 | 指数符号位 | 指数位 | 有效数位 |
有效位数位为24位,其中一个是实数符号位。
>>doublie型在内存中占8个字节,64个bit位,double的64位二进制结构如下:
63 | 62 | 61-52 | 51-0 |
实数符号位 | 指数符号位 | 指数位 | 有效数位 |
有效位数位为52位,其中一个是实数符号位。
二、将一个float型转化为内存存储格式
(以11.9为例)
<1>将这个实数化为二进制格式,注意实数的整数部分和小数部分的二进制转化;
参考https://blog.csdn.net/sophie1314/article/details/84287948
(11.9转化为二进制:1011.1110011001100....)
<2>将这个二进制格式实数的小数左移或右移n位,直到小数点移动到第一个有效数字的右边;
(则1011.1110011001100....变为1.0111110011001100...)
<3>有效数位:从小数点右边第一位开始,数出23位有效数字放到第22-0位;(产生误差)
(1.0111110011001100....右边取23位,为0111 1100 1100 1100 1100 110)
<4>实数符号位:如果实数是正的,则在第31位放入0,否则放入1;
(11.9是正的,所以放0)
<5>指数符号位:如果n是左移得到的,则表示n是正的,在30位放入1,否则放入0;
(左移三位,为正,放入1)
<6>指数位:如果n是左移得到的,则n-1之后化为二进制,并在左边加“0”补足7位;如果是右移得到的或者n=0,则化为二进制左边加“0”补足7位,然后再各位求反,然后再放入29-23位。
(左移得到的,3-1=2,0000010)
所以,最终得到11.9在内存中存储的是0 1 0000010 01111100110011001100110
三、将一个内存存储格式的float二进制转化为十进制
<1>取出后面23位,在前面加“1.”,得到24位有效位数(如果十进制化二进制时产生误差,那么二进制化十进制也会有误差)
<2>从左数取第一位,0表示正数,1表示负数
<3>从左数取第二位,1表示指数为正,0表示指数为负
<4>若第二位数为1,则从左数第3位到第9位,化为十进制加1得到n,24位有效位数小数点右移n位,化为十进制即得到最终结果;若第二位数位0,则先求反然后再化为十进制数为n,24位有效位数左移n位小数点,化为十进制即得到最终结果。
四、浮点型的减法运算
<1>检查0操作数:如果浮点数中有一个为0,即可得知运算结果而没有必要再进行有序的一些列操作。
<2>比较界码大小并完成对阶,看两数的指数位是否相同,即小数点位置是否对齐。若两数指数位相同,表示小数点是对齐的,就可以进行尾数的加减运算。反之,若两数阶码不同,表示小数点位置没有对齐,此时必须通过进行尾数的移位,使两数的阶码相同,这个过程叫做对阶 。
<3>有效位数进行加减法运算
对阶完毕后就可以对有效数位求和。不论是加法运算还是减法运算,都按加法进行操作,其方法与定点加减运算完全一样。
<4> 结果规格化并进行舍入处理。
五、计算12.0f-11.9f
12.0f 的内存存储格式为: 0 1 0000010 10000000000000000000000
11.9f 的内存存储格式为: 0 1 0000010 011 11100110011001100110
可见两数的指数位完全相同,只要对有效数位进行减法即可。
12.0f-11.9f 结果: 0 1 0000010 00000011001100110011010
将结果还原为十进制为: 0.000 11001100110011010= 0.10000038