一、概况float 32位数据,8位用于存储指数,24位存储尾数。
基本参数从cfloat,climits(float.h)中查看:
#define FLT_DIG 6
#define FLT_MANT_DIG 24
#define FLT_MAX_10_EXP +38
#define FLT_MIN_10_EXP -38
分别表示,有效数字位数,尾数位数,指数最大,最小值。
二 、将一个float型转化为内存存储格式的步骤为:
(1)先将这个实数的绝对值化为二进制格式。
(2)将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边。
(3)从小数点右边第一位开始数出二十三位数字放入第22到第0位。
(4)如果实数是正的,则在第31位放入“0”,否则放入“1”。
(5)如果n 是左移得到的,说明指数是正的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”。
(6)如果n是左移得到的,则将n减去1后化为二进制,并在左边加“0”补足七位,放入第29到第23位。如果n是右移得到的或n=0,则将n化为二进制后在左边加“0”补足七位,再各位求反,再放入第29到第23位。
三、将一个内存存储的float二进制格式转化为十进制的步骤:
(1)将第22位到第0位的二进制数写出来,在最左边补一位“1”,得到二十四位有效数字。将小数点点在最左边那个“1”的右边。
(2)取出第29到第23位所表示的值n。当30位是“0”时将n各位求反。当30位是“1”时将n增1。
(3)将小数点左移n位(当30位是“0”时)或右移n位(当30位是“1”时),得到一个二进制表示的实数。
(4)将这个二进制实数化为十进制,并根据第31位是“0”还是“1”加上正号或负号即可。
float存储如下所示: 1位S ,8位E,23位F
其中F= 1.f(22)....f(0)
E= e - Bias(127)
X = F*2^E
四、代码与详细说明:
1. 其中S位占1bit,为bit31看,S位为0代表浮点数是正数,S位为1代表浮点数是负数。
2. E位占8bits,为bit23~bit30。E位代表2的N次方,但需要减去127(移位存储,后面再解释),比如说E位为87,那么E位的值为2(87-127)=9.094947017729282379150390625e-13。
3. F位占23bits,为bit0~bit22。F位是小数点后面的位数,其中bit22是2^-1=0.5,bit21是2^-2=0.25,以此类推,bit0为2^-23=0.00000011920928955078125。但F位里隐藏了一个1,也就是说F位所表示的值是1+(F位bit22~bit0所表示的数值),比如说F位是(0b)10100000000000000000001,只有bit22、bit20和bit0为1,那么F位的值为1+(2^-1+2^-20+2^-22),为1.5000011920928955。
代码与分析:
#include <iostream>
using namespace std;
int main()
{
float a = 100;
cout<<a<<endl;
unsigned char *b;
b = (unsigned char *)&a;
for(unsigned int i=0;i<4;++i){
cout<<hex<<static_cast<int>(b[i])<<endl;// 检测转换保证安全
}
cin.get();
}
100 ->float S = 0, e = 0x85 ,f = (计算需补1)100100(100 = 0x64 = 1100100 = 1.100100*2^6)
E = e - Bias = e - 2^7 +1= 6,F = 1.f = 0xC800
E*2^F = 100;
五、浮点数的精度
首先考虑float中的定义 FLT_EPSILON,意思为 “正float的0跨度值”,即满足 x+0.0 != 0.0 的最小正整数。
在32位系统中,该值等于 2^-23 约等于1.1920928955078125e-07 < 1.0e-6
该值计算可以通过:指数= 0 ,尾数取最小值来获得,即2^-23(f0取1其他取0)
由此,引申而来对float非0的判断或两数字相等判断,if(abs( a- b) < FLT_EPSILON)
六、指数取得38的原理
指数7位有效数字,指数原本-2^7-1 ~ 2^7由于bias的存储方式使得其存储内容为,-2^7~2^7 +1
然后2^7 + 1 相当于128,然后2^128,对应可以取得3.4*10^38,对应于S位的正负,因此float 的一个最大范围就是-3.4e+38~3.4e+38
而对应的最接近0,的非0数应该是,让指数取最负值,即-127,然后尾数取f=0,这个值大概为5.8e-39.
七、为何偏置非规范化值
1. 提供一种表示0值的方法,使用规格化M>=1而无法表示1,实际+0.0表示全0,M=f=0.符号位为1时,其他域为0,得到-0.0,偏置后使得只有一个0.
2. 那些非常接近0.0的数,提供了一种属性,逐渐溢出(gradual underflow),可能的数值分布均匀的接近0.0
八、特殊值
重要:指数全为1。
小数域全0,得到无穷,s=0,+无穷,s=1,-无穷。两大数相乘,或者除0,无穷可表示溢出结果。
小数域非0,NaN,一些运算的结果不能是实数或无穷,就会返回这样的NaN值,比如计算根号-1时候。(判断可以f!=f,或者isnan())