书籍:《ISO/IEC 14882 2014》
说明:以上内容大部分来自腾讯元宝
在C++中,float
、double
和long double
是三种浮点类型,其基本结构遵循IEEE 754标准,但具体实现细节因编译器和平台而异。以下是它们的详细结构解析:
一、float(单精度浮点数)
1. 存储结构
- 总位数:32位(4字节)
- 位域分配:
位域 位数 描述 符号位(S) 1 0表示正数,1表示负数 指数位(E) 8 采用偏移量127的移码表示(实际指数值 = 存储值 - 127) 尾数位(M) 23 存储小数部分的二进制展开(隐含前导1,实际有效位数为24位)
2. 数值范围与精度
- 范围:±1.4×10⁻⁴⁵ ~ ±3.4×10³⁸
- 精度:约6-7位十进制有效数字
- 示例:
3.14159f
在内存中表示为:
0 10000000 10010001111010111000011
(符号位0,指数128-127=1,尾数1.10010001111010111000011)
二、double(双精度浮点数)
1. 存储结构
- 总位数:64位(8字节)
- 位域分配:
位域 位数 描述 符号位(S) 1 同上 指数位(E) 11 偏移量1023(实际指数值 = 存储值 - 1023) 尾数位(M) 52 隐含前导1,实际有效位数为53位
2. 数值范围与精度
- 范围:±4.9×10⁻³²⁴ ~ ±1.8×10³⁰⁸
- 精度:约15-16位十进制有效数字
- 示例:
3.141592653589793
在内存中表示为:
0 10000000000100100 111101011100001100110100010001000010110100011
三、long double(扩展精度浮点数)
1. 存储结构
- 总位数:平台依赖(常见为80位或128位)
- x86架构:80位(10字节),但通常以16字节(128位)对齐存储。
- GCC/Clang:可能支持80位扩展精度(
__float128
)。
- 位域分配(以80位为例):
位域 位数 描述 符号位(S) 1 同上 指数位(E) 15 偏移量16383(实际指数值 = 存储值 - 16383) 尾数位(M) 64 隐含前导1,实际有效位数为65位
2. 数值范围与精度
- 范围:±3.4×10⁻⁴⁹³² ~ ±1.1×10⁴⁹³²
- 精度:约18-19位十进制有效数字
- 示例(80位格式):
3.14159265358979323846
可能存储为:
0 1000011111110000 111101011100001100110100010001000010110100011000000000
四、关键差异对比
属性 | float | double | long double |
---|---|---|---|
存储空间 | 4字节 | 8字节 | 8/16字节(平台依赖) |
符号位 | 1位 | 1位 | 1位 |
指数位 | 8位(偏移127) | 11位(偏移1023) | 15位(偏移16383) |
尾数位 | 23位(隐含1) | 52位(隐含1) | 64位(隐含1) |
有效位数 | 6-7位十进制 | 15-16位十进制 | 18-19位十进制 |
典型用途 | 内存敏感场景 | 通用科学计算 | 高精度数学运算 |
五、内存布局示例
1. float示例:1.5f
- 二进制科学计数法:
1.1 × 2¹
- 存储值:
- 符号位:
0
- 指数位:
10000000
(128,实际指数128-127=1) - 尾数位:
10000000000000000000000
(隐含前导1后为1.1
)
- 符号位:
2. double示例:√2 ≈ 1.4142135623730951
- 二进制科学计数法:
1.0110101000001001111001100110011111111010
- 存储值:
- 符号位:
0
- 指数位:
1000000000001011
(1024 + 11 = 1035,实际指数1035-1023=12) - 尾数位:
0110101000001001111001100110011111111010
(隐含前导1)
- 符号位:
六、特殊值表示
值类型 | 符号位(S) | 指数位(E) | 尾数位(M) |
---|---|---|---|
正零 | 0 | 全0 | 全0 |
负零 | 1 | 全0 | 全0 |
正无穷大 | 0 | 全1 | 全0 |
NaN | 0/1 | 全1 | 非全0 |
七、跨平台注意事项
-
long double
的差异:- MSVC中与
double
同为8字节,GCC/Clang可能支持80位扩展精度。
- MSVC中与
- 对齐问题:
- 结构体中包含
long double
时,可能因对齐导致内存浪费(如填充字节)。
- 结构体中包含
- 输出控制:
- 使用
<iomanip>
库的setprecision
和fixed
确保跨平台一致性。
- 使用
八、代码验证
#include <iostream>
#include <iomanip>
int main() {
std::cout << std::boolalpha;
std::cout << "float size: " << sizeof(float) << " bytes\n";
std::cout << "double size: " << sizeof(double) << " bytes\n";
std::cout << "long double size: " << sizeof(long double) << " bytes\n";
// 输出内存布局(以float为例)
float f = -123.456f;
unsigned int* p = reinterpret_cast<unsigned int*>(&f);
std::cout << "float binary: " << std::hex << *p << std::dec << "\n";
return 0;
}
输出示例:
float size: 4 bytes
double size: 8 bytes
long double size: 16 bytes
float binary: 42f6e979
总结
- float:适合内存敏感场景,但精度有限。
- double:通用选择,平衡精度与性能。
- long double:高精度需求场景,但需注意平台兼容性。
通过理解内部结构,可以更好地控制浮点数的精度和存储行为。