文章参考自:浮点数的二进制表示(IEEE 754标准)
转载地址:https://blog.csdn.net/piglite/article/details/124586693
1. 小数用二进制如何表示
首先,给出一个任意实数,整数部分用普通的二进制便可以表示,这里只说小数部分如何表示
例如0.6
将该数字乘以2,取出整数部分作为二进制表示的第1位;然后再将小数部分乘以2,将得到的整数部分作为二进制表示的第2位;以此类推,直到小数部分为0。
特殊情况: 小数部分出现循环,无法停止,则用有限的二进制位无法准确表示一个小数,这也是在编程语言中表示小数会出现误差的原因
下面具体看一下小数0.6用二进制表示的计算过程
0.6 * 2 = 1.2 ——————- 1
0.2 * 2 = 0.4 ——————- 0
0.4 * 2 = 0.8 ——————- 0
0.8 * 2 = 1.6 ——————- 1
0.6 * 2 = 1.2 ——————- 1
…………
我们可以发现在该计算中已经出现了循环,0.6用二进制表示为0.1001 1001 1001 1001 ……
如果是10.6,那个10.6的完整二进制表示为 1010.100110011001……
2. 二进制表示的小数如何转换为十进制
再拿0.6的二进制表示举例:0.1001 1001 1001 1001
文字描述:从左到右,v[i] * 2^( - i ), i 为从左到右的index,v[i]为该位对应的值,看例子:
0.6 = 1 * 2^-1 + 0 * 2^-2 + 0 * 2^-3 + 1 * 2^-4 + ……
3. 浮点数在计算机内存中的存储
浮点数是在内存中到底是怎么样保存的呢?
现代计算机中,一般都以IEEE 754标准存储浮点数,这个标准的在内存中存储的形式为:
对于不同长度的浮点数,阶码与小数位分配的数量不一样,如下:
对于32位的单精度浮点数,数符分配是1位,阶码分配了8位,尾数分配了是23位。而对于64位的双精度浮点数,数符分配是1位,阶码分配了11位,尾数分配了是52位
根据这个标准,我们来尝试把一个十进制的浮点数转换为IEEE754标准表示。
例如:178.125
先把浮点数分别把整数部分和小数部分转换成2进制
- 整数部分用除2取余的方法,求得:10110010
- 小数部分用乘2取整的方法,求得:001
合起来即是:10110010.001
转换成二进制的浮点数,即把小数点移动到整数位只有1,即为:1.0110010001 * 2^111,111是二进制,由于左移了7位,所以是111,阶数即为111。
把浮点数转换二进制后,这里基本已经可以得出对应3部分的值了
- 数符:由于浮点数是正数,故为0(负数为1)。
- 阶码 : 阶码需要作移码运算,阶码的计算公式是:阶数 + 偏移量。在转换出来的二进制数里,阶数是111(十进制为7),对于单精度的浮点数,偏移值为01111111(127)[注:偏移量的计算方法是:2^(e-1)-1 ,其中e为阶码的位数,单精度浮点数阶码位数为8,因此偏移值是127],即:111+01111111 = 10000110,当然也可以按10进制计算,即127+7 = 134,再将134转为8位二进制就是10000110。
- 尾数:小数点后面的数,即0110010001
最终根据位置填到对位的位置上:
可能有个疑问:小数点前面的1去哪里了?由于尾数部分是规格化表示的,最高位总是“1”,所以这是直接隐藏掉,同时也节省了1个位出来多存1位尾数,提高精度。所以32位浮点数可以表示的精度是24位,64位浮点数可以表示的精度是53位。2进制24位精度转为10进制是7.22位(24*lg2),而53位精度转为10进制是15.95(53*lg2)。
在v2017中打开代码调试:
再试一下0.6吧,0.6转为二进制是0.10011001100110011001...,用二进制表示0.6只能无限接近,但是不能精确表示。现在为了满足尾数的要求,需要右移一位小数点,所以是2^(-1),然后偏移127转为8位二进制就是01111110,尾数去掉1并保留23位,在处理到最后一位的时候按照二进制的“四舍五入”规则,也就是零舍一入,此时的23位尾数就是00110011001100110011010(结尾的本来四个数字1001,再下一位是1,按照零舍一入原则,从1001入为1010)。
所以用float存储0.6的格式就是:
在v2017中打开代码调试:
最后再试试负数-10.6
10.6转为二进制:1010.100110011001……
所以符号位1
阶数3偏移127为130,转为8位二进制是10000010
尾数保留23位,最后一位按照零舍一入原则处理是01010011001100110011010
所以用float保存-10.6就是:
在v2017中打开代码调试:
最后是关于float和double取值范围的内容,IEEE754中浮点数的计算公式是:
s 是符号位的数字,如果 s 为0则浮点数V是整数,如果 s 为 1,则浮点数 V 为负数。
M 表示有效数字[1,2),而 E 是阶数位,float的E是8,而double的E是11。
阶数不包括全零和全一的情况(阶数为0,代表数字0;阶数为全1,代表无穷大),偏置常数是127,因此它的正常取值范围是-126-127(这样偏移后的数字是1~254)。尾数值等于1+尾数23位表示的小数,所以原则上尾数可取的范围是1(后面全零)到1.111...(后面全1),1.111...可表示为2-2^-23,可近似约等于2。
于是对于正浮点数:
对于负浮点数:
如果粗略的只取最大空间,那么float类型的取值范围是-3.4E38~3.4E38。
64位浮点数的计算方式与之类似,阶数不包括全零和全一的情况,偏置常数是1023,因此它的取值范围是-1022 - 1023(偏移后的数字是1~2046),尾数范围是1.0~2-2^-52,所以计算公式是:
对于正浮点数
对于负浮点数:
如果粗略的只取最大空间,那么double类型的取值范围是-1.79E308~1.79E308。