long double
long double 是 C 语言中精度最高的浮点类型,提供比 double 更大的取值范围和更高的精度。但具体大小和精度由实现定义——在某些平台上它与 double 相同(64 位),在另一些平台上可能是 80 位扩展精度或 128 位四精度。使用 long double 时,需要了解当前平台的实际特性。
基本用法
long double 的声明和使用与 float、double 类似,常量后缀为 L(大写,避免与数字 1 混淆):
long double pi = 3.14159265358979323846264338327950288L;
long double precise = 1.0e-400L; /* 可以表示极小的数 */
printf("sizeof(long double) = %zu\n", sizeof(long double));
/* x86 GCC:12 或 16(80 位扩展精度,对齐到 12 或 16 字节) */
/* x86-64 GCC:16 */
/* MSVC:8(与 double 相同) */
范围与精度
<float.h> 中定义了 long double 的特征值:
#include <float.h>
#include <stdio.h>
int main(void)
{
printf("LDBL_MIN = %Le\n", LDBL_MIN); /* 最小正值 */
printf("LDBL_MAX = %Le\n", LDBL_MAX); /* 最大正值 */
printf("LDBL_EPSILON = %Le\n", LDBL_EPSILON); /* 最小精度 */
printf("LDBL_DIG = %d\n", LDBL_DIG); /* 有效十进制位数 */
return 0;
}
典型值对比:
| 平台 | 大小 | 有效位数 | 指数范围 |
|---|---|---|---|
| x86/x86-64 GCC | 80 位(16 字节对齐) | 约 18-19 位 | ±10⁴⁹³² |
| MSVC | 64 位(与 double 相同) | 约 15 位 | ±10³⁰⁸ |
| ARM | 128 位(四精度) | 约 33 位 | ±10⁴⁹³² |
由于平台差异,long double 的可移植性不如 double。如果代码需要在 Windows 和 Linux 之间移植,且依赖 long double 的扩展精度,需要特别测试。
格式化输入输出
printf 和 scanf 使用 %Lf(大写 L)格式说明符:
long double value = 1.234567890123456789L;
printf("%Lf\n", value); /* 默认 6 位小数 */
printf("%.18Lf\n", value); /* 18 位小数 */
printf("%Le\n", value); /* 科学计数法 */
printf("%La\n", value); /* C99:十六进制浮点 */
/* 读取 */
long double input;
scanf("%Lf", &input);
注意:%Lf 中的 L 是大写。小写 %lf 在 printf 中等价于 %f(因为 float 会自动提升为 double),但在 scanf 中 %lf 对应 double,%Lf 对应 long double。
何时使用 long double
需要极高精度的科学计算:
/* 计算 π 到高精度 */
long double pi = 0.0L;
for (int i = 0; i < 1000000; i++) {
long double term = (i % 2 == 0 ? 1.0L : -1.0L) / (2 * i + 1);
pi += term;
}
pi *= 4.0L;
printf("π ≈ %.20Lf\n", pi);
避免中间计算精度损失:
/* 大数与小数相加时,double 可能丢失小数部分 */
double big = 1e20;
double small = 1.0;
double sum_d = big + small; /* 结果仍是 1e20,small 被忽略 */
long double big_ld = 1e20L;
long double small_ld = 1.0L;
long double sum_ld = big_ld + small_ld; /* 保留精度 */
金融计算中的累积精度:
/* 大量小额交易的累积 */
long double total = 0.0L;
for (int i = 0; i < 10000000; i++)
total += 0.01L; /* 使用 long double 减少累积误差 */
printf("Total: %.10Lf\n", total);
注意事项
性能开销:long double 的运算通常比 double 慢,因为硬件支持程度不同。x86 的 80 位扩展精度由 FPU 直接支持,但 x86-64 的 SSE/AVX 单元只原生支持 64 位 double,128 位运算需要软件模拟。
与 double 混合运算:
long double a = 1.0L;
double b = 2.0;
long double c = a + b; /* b 先提升为 long double,再相加 */
混合运算时,较低精度的操作数会先提升为 long double,结果也是 long double。
可移植性建议:如果代码不依赖 long double 的扩展精度,使用 double 是更安全和可移植的选择。如果确实需要,通过 <float.h> 的 LDBL_DIG 等宏在运行时检查精度是否满足需求。
常见错误
格式说明符错误:
long double x = 1.0L;
printf("%f\n", x); /* 错误:%f 对应 double,x 是 long double */
printf("%lf\n", x); /* 错误:printf 中 %lf 等价于 %f */
printf("%Lf\n", x); /* 正确 */
常量后缀遗漏:
long double a = 3.14159; /* 3.14159 是 double,可能损失精度 */
long double b = 3.14159L; /* 正确:long double 常量 */
假设大小:
/* 错误假设 long double 总是 80 位或 128 位 */
assert(sizeof(long double) == 16); /* 在 MSVC 上会失败! */