浮点数数据在计算机中的是按照特定的编码格式进行存储的,下面我们就以float数据-20.5来分析一下浮点数的存储格式。
目录
结构组成
浮点数结构分为三个部分:符号码、指数码、尾数码。
IEEE 754 标准分 3 个类型的浮点数:分别是 短浮点数 float,长浮点数 double,临时短浮点数 long double,存储结构如下:
符号码
符号码占用1个bit,占用最高位,对于-20.5数值,符号位为1,代表为负数。对于浮点数符号判断,可以使用std::signbit进行判断,详细见:std::signbit - cppreference.com。
指数码
指数码是最为复杂的字段,指数码表示尾数码偏移的二进制的位数。在 IEEE 754 浮点数标准中,指数码是用移码表示的,移码的计算方式:移码 = 真值 + 偏置值;在浮点数标准中偏置值为 2^(8-1)-1 = 127。如图所示,131=127+4;因此-20.5的指数码为4。
尾数码
在 IEEE 754 浮点数标准中,尾数码部分采用原码表示,且尾数码隐含了最高位 1,在计算时我们需要加上最高位1,即 1.M,我们通过一个例子来表示:
1.01001B = 1 + 0* + 1*
+ 0*
+ 0*
+ 1*
=1.28125
1.01001B = 1 + 0*0.5 + 1*0.25 + 0*0.125+ 0*0.0625 + 1*0.03125 =1.28125
十进制转化为浮点数
接下来咱们就使用-20.5D看看怎样一步一步转化为内存中显示的方式的。
-20.5D分为三个部分:
-1.0先不进行处理,20D转化为二进制为10100B,0.5D转化为二进制为0.1B,因此20.5D转化为二进制为:10100.1B,将整数部分转化为1,将二进制数据右移4位:1.01001B*=10100.1B隐藏整数部分的1,因此尾数码为0.01001B。指数为4,转化为偏移码:4+127=131D,转化为二进制:10000011B。
-20.5D转化为浮点数进行存储时为:符号码1<<31+指数码(10000011B)<<23 + 尾数码(01001000000000000000000B) =11000001101001000000000000000000B。
数值计算value = -1.0(符号位)*(指数位:2的4次方)*1.28125 (位数:1(被隐藏)+0.28125)= -20.5;
浮点数范围
-
绝对值最小值
IEEE 754 单精度浮点型表示的的最小绝对值:尾数码全为 0 ,阶码真值为 -126D(对应的移码为 0000 0001B),此时整体的值为 1.0B * 2^(-126D)
-
绝对值最大值
IEEE 754 单精度浮点型表示的的最大绝对值:尾数码全为 1 ,阶码真值为 127D(对应的移码为 1111 1110B),此时整体的值为 1.111…B * 2^(127D)
-
指数码真码特殊值
一般我们正常探讨的指数码真码区间是 -126D ~ 127D,而真值 -127D 的阶码为 0000 0000B,真值 128D 的阶码为 1111 1111B。
- 指数码真值为 -127
当指数码全为 0 ,尾数不全为 0,表示 非规格化小数 ,用来表示比最小绝对值还要小的数,即
尾数码 隐含的最高位不是 1,而是 0;
当指数码全为 0 ,尾数全为 0,表示 真值 +/- 0 ;
-
指数码真码128
当阶码全为 1 ,尾数全为 0,表示 正负无穷大 +/- ∞
当阶码全为 1 ,尾数不全为 0,表示非数值 NaN(Not a Number)
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
float a = 0.11;
float b = -20.5;
float c = -0.00000001234567;
float d = -1.123456789;
float e = 12.123456789;
float f = -123.123456789;
float g = -0.0;
float h = 0.0;
int size = sizeof (b);
bool sigB = std::signbit(b);
bool sigE = std::signbit(e);
bool sigG = std::signbit(g);
bool sigH = std::signbit(h);
std::cout << "sigB: " << sigB << " , sigE : " << sigE << std::endl;
std::cout << "sigG: " << sigG << " , sigH : " << sigH << std::endl;
return 0;
}
浮点数精度问题
由上图可知,对于float类型数据来说,可以表示7位精度的数据,并不是表示小数点后7位数据。std::numeric_limits<float>::epsilon() EPSILON指的是浮点数可表示的最小值,有没有比EPSILON更小的数呢?答案是肯定的。EPSILON被规定为是最小误差,换句话说就是使得EPSILON+1.0不等于1.0的最小的正数,也就是如果正数d小于EPISILON,那么d和1.0相加,计算机就认为还是等于1.0,这个EPISILON是变和不变的临界值。EPSILON被规定为是最小误差的前提是EPSILON加上一个大于1.0的数与不加EPSILON是相等的,但是如果是两个EPSILON相加肯定大于EPSILON。
参考资料:
IEEE754 浮点数:简读+案例=秒懂_ieee754浮点数的计算-优快云博客
IEEE754标准: 一 , 浮点数在内存中的存储方式 - 知乎
std::signbit:std::signbit - cppreference.com
std::numeric_limits的使用:std::numeric_limits的使用_std::numeric_limits<float>::epsilon()-优快云博客
IEEE-754 Floating Point Converter:IEEE-754 Floating Point Converter