float的存储
float的存储遵循IEEE标准,一个float4字节32位,被分成三个部分:
s(1位)表示符号位,0表示正数,1表示负数;
M(8位)表示指数位即小数点移动的位数,这里引入了一个余码系统后面会分析;
E(23位)表示尾数部分,转成二进制表示1.X;
余码系统
在开始了解存储之前我们有必要了解一下余码系统,我们以四位来举例说明,四位能表示的有符号整数的范围是[1111,0111],考虑正负零的情况我们认为可以表示[-7,8]
(和inr范围求法略有不同),我们让每个数+7,这样表示的范围就是[0,15],而四位表示的无符号范围恰好也是[0,15],所以我们无论有符号无符号的数都按无符号来存储,只不过求他的真实值的时候减去一个偏移量(在这是7)。
浮点数的指数存储就采用这样一种余码系统,不同的是它占8位,所以是一种余127码,(偏移127位)。
实例
3.625的二进制是怎么存储的,先考虑3的二进制11(除2取余),再考虑0.625的二进制101(乘2取整),所以(3.625)10 = (11.101),我们类比科学计数法将11.101转换成1.1101*2^1,所以我们只要分两部分存储,底数1.1101和指数1。
我们用23位来存储底数,1110 1000 0000 0000 0000 000,考虑到每个数转换为科学计数法之后每个数第一位必是1,所以我们从第二位开始存储,1101 0000 0000 0000 0000 000。
用8位来存储指数,再结合余127码,他应该存储1+127 即1000 0000。
再加上符号位0,最后结果是0100 0000 0110 1000 0000 0000 0000 0000转换成16进制就是0x40680000。
#include <stdio.h>
int main()
{
float a = 3.625;
printf("%0x\n",*(int*)&a);
return 0;
}
float的运算
#include <stdio.h>
int main()
{
if(0.42+0.1 == 0.43){
printf("==\n");
}else{
printf("!=\n");
}
if(17.625+1 == 18.625){
printf("==\n");
}else{
printf("!=\n");
}
return 0;
}
运行结果
!=
==
我们以1.0F-0.42F来举例子说明浮点数的运算规则
1.0F存储 0111 111 1000 0000 0000 0000 0000 0000
0.42F存储 0111 110 1101 0111 0000 1010 0011 1101
仔细观察我们我们将0.42F的二进制转换回十进制是0.4199999868869781494140625,这就是精度损失的情况之一,二者相减E位要相统一,都与较大的E保持一致,所以0.42F的存储变为了0011 1111 1011 0101 1100 0010 1000 1111,再仔细观察就可以发现,M位在后移的过程中丢失了最后两位,这也是精度损失的情况,二者相加减得到100 1010 0011 1101 0111 0001,转换10进制0.58000004291534423828125。