c/c++ 中 double 和 float 在内存中的存储结构


浮点数,是指小数点在数据中的位置可以左右移动的数据。

计算机内存中都是以二进制 (0 或 1) 的形式储存数据, 每个 0 或 1 被称为 1 位(bit), 在 x86 CPU 中 1 字节(byte)为 8 位(bit);
double 和 float 这两种精度不同的地方是 float 是 4 字节即 32 位, double 是占 8 字节为 64 位。

目前 C/C++ 编译器遵照 IEEE-754(IEEE Standard for Floating-Point Arithmetic) 制定的浮点数表示法来进行 float, double 运算。在计算机中, 浮点数通常被表示成以下格式:

|__S__|____E____|________M________|

即为 符号位(Sign) + 指数位(Exponent也称阶码) + 尾数(Mantissa)。 对于 float 型数据(32 位), 其中符号位 1 位, 指数位为 8 位, 尾数 23 位; 对于 double 型数据(64 位), 其中符号位 1 位, 指数位 11 位, 尾数52位。
具体的规格:

类型 长度 符号位(Sign) 指数位(Exponent) 尾数(Mantissa)
float 32 bits 1 bits (31) 8 bits (30 – 23) 23 bits (22 – 0)
double 64 bits 1 bits (63) 11 bits (62 – 52) 52 bits (51 – 0)

符号位(Sign): 0 表示数值为正数, 1 表示负数

指数位(Exponent): 即为二进制用科学计数法表示的指数部分 (它指出的是小数点在数据中的位置, 决定了浮点数的表示范围), 其中 float 为 8 位, double 为 11 位。 以 float 为例, 8 位的指数为可以表达 0 到 255 之间的 255 个指数值。但是指数可以为正数, 也可以为负数。为了能表示负指数, 实际存储指数值需要加上一个偏移量(Bias)作为保存在指数位中的值。 float 偏移量为 127 (即 8 位指数可以表示 -127 ~ 128 次方), 而 double 的偏移量为 1023 (即 11 位指数可以表示 -1023 ~ 1024 次方, 所以 double 实际可以表示的最大值为 2 ^ 1024,所以可以表示的范围为 -1.7E308 ~ 1.7E308)。
举个栗子, float 实际指数数值为 0 在指数位中将保存为 127, double 则存为 1023; 再如 double 的真实指数为 3, 则需加 1023 即为 1026, 存为 10000000010; 若为 float 则需加 127 即 130, 表示为 10000010

尾数(Mantissa): 称为浮点数的尾数, 即部分二进制位(小数点后面的二进制位, 它决定了浮点数的表示精度, 即可以给出的有效数的位数), 因为规定 M 的整数部分恒为1, 所以这个 1 就不进行存储。

这里有个特例, 浮点数为 0 时, 理论上指数和底数都为 0, 但此前的公式不成立, 因为 2 的 0 次方为 1, 所以 0 是个特例。所以当尾数为 0 时, 不论其指数为何值, 该浮点数被认为是 0;

举个大的栗子:
float -3.1415926:

11000000 01001001 00001111 11011010
c0       49       0f       da       

可以看出第 1 位符号位为 1 表示这是一个负数
float 接下来 8 位 1000000 0 为指数位, 值 128, 减掉偏移量 127 则为 1 位
底数部分
1.1001001 00001111 11011010 小数点向右移指数 1 位则
11.001001 00001111 11011010 整数部分转十进制为

1 * 2 ^ (2 - 1) + 1 * 2 ^ (1 - 1) = 3

小数部分 0.001001 00001111 11011010 转为 10 进制为

1 * 2 ^ -3 + 1 * 2 ^ -6 + 1 * 2 ^ -11 + .... = 0.14159250259399

所以此 float 值为

3 + 0.14159250259399 = 3.14159250259399

double -3.1415926:

11000000 00001001 00100001 11111011 01001101 00010010 11011000 01001010 
c0       09       21       fb       4d       12       d8       4a    

同样可以看出第 1 位符号位为 1 表示这是一个负数
double 接下来 1 位 1000000 0000 为指数位, 值 1024, 减掉偏移量 1023 则为 1 位
底数部分
1.1001 00100001 11111011 01001101 00010010 11011000 01001010 小数点向右移指数 1 位则
11.001 00100001 11111011 01001101 00010010 11011000 01001010 整数部分转十进制为

1 * 2 ^ (2 - 1) + 1 * 2 ^ (1 - 1) = 3

小数部分 0.001001 00001111 11011010 转为 10 进制为

1 * 2 ^ -3 + 1 * 2 ^ -6 + 1 * 2 ^ -11 + .... = 0.1415926

所以此 double 值为

3 + 0.1415926 = 3.1415926

由这两个”同样”的数 3.1415926 不同类型表示可以看出, double 比 float 表示的更精确