Appearance
C 标准库 - <float.h>
概述
<float.h> 头文件定义了浮点数类型的限制和参数,包括精度、最小值、最大值等信息。这些宏可以帮助开发者了解浮点数的特性,编写更可靠的数值计算程序。
浮点数类型
C 语言支持三种浮点数类型:
- float - 单精度浮点数(通常 32 位)
- double - 双精度浮点数(通常 64 位)
- long double - 扩展精度浮点数(通常 80 位或 128 位)
基本参数宏
FLT_RADIX
浮点数表示的基数(通常为 2)。
c
#include <stdio.h>
#include <float.h>
int main() {
printf("浮点数基数: %d\n", FLT_RADIX);
return 0;
}FLT_MANT_DIG, DBL_MANT_DIG, LDBL_MANT_DIG
浮点数的尾数位数(精度)。
c
#include <stdio.h>
#include <float.h>
int main() {
printf("float 尾数位数: %d\n", FLT_MANT_DIG);
printf("double 尾数位数: %d\n", DBL_MANT_DIG);
printf("long double 尾数位数: %d\n", LDBL_MANT_DIG);
return 0;
}FLT_DIG, DBL_DIG, LDBL_DIG
十进制精度位数(可以精确表示的十进制数字位数)。
c
#include <stdio.h>
#include <float.h>
int main() {
printf("float 十进制精度: %d\n", FLT_DIG);
printf("double 十进制精度: %d\n", DBL_DIG);
printf("long double 十进制精度: %d\n", LDBL_DIG);
return 0;
}FLT_MIN_EXP, DBL_MIN_EXP, LDBL_MIN_EXP
最小指数值(以 FLT_RADIX 为基数)。
c
#include <stdio.h>
#include <float.h>
int main() {
printf("float 最小指数: %d\n", FLT_MIN_EXP);
printf("double 最小指数: %d\n", DBL_MIN_EXP);
printf("long double 最小指数: %d\n", LDBL_MIN_EXP);
return 0;
}FLT_MAX_EXP, DBL_MAX_EXP, LDBL_MAX_EXP
最大指数值(以 FLT_RADIX 为基数)。
c
#include <stdio.h>
#include <float.h>
int main() {
printf("float 最大指数: %d\n", FLT_MAX_EXP);
printf("double 最大指数: %d\n", DBL_MAX_EXP);
printf("long double 最大指数: %d\n", LDBL_MAX_EXP);
return 0;
}FLT_MIN_10_EXP, DBL_MIN_10_EXP, LDBL_MIN_10_EXP
最小十进制指数值。
c
#include <stdio.h>
#include <float.h>
int main() {
printf("float 最小十进制指数: %d\n", FLT_MIN_10_EXP);
printf("double 最小十进制指数: %d\n", DBL_MIN_10_EXP);
printf("long double 最小十进制指数: %d\n", LDBL_MIN_10_EXP);
return 0;
}FLT_MAX_10_EXP, DBL_MAX_10_EXP, LDBL_MAX_10_EXP
最大十进制指数值。
c
#include <stdio.h>
#include <float.h>
int main() {
printf("float 最大十进制指数: %d\n", FLT_MAX_10_EXP);
printf("double 最大十进制指数: %d\n", DBL_MAX_10_EXP);
printf("long double 最大十进制指数: %d\n", LDBL_MAX_10_EXP);
return 0;
}最小值和最大值
FLT_MIN, DBL_MIN, LDBL_MIN
最小正规格化浮点数。
c
#include <stdio.h>
#include <float.h>
int main() {
printf("float 最小值: %e\n", FLT_MIN);
printf("double 最小值: %e\n", DBL_MIN);
printf("long double 最小值: %Le\n", LDBL_MIN);
return 0;
}FLT_MAX, DBL_MAX, LDBL_MAX
最大有限浮点数。
c
#include <stdio.h>
#include <float.h>
int main() {
printf("float 最大值: %e\n", FLT_MAX);
printf("double 最大值: %e\n", DBL_MAX);
printf("long double 最大值: %Le\n", LDBL_MAX);
return 0;
}FLT_EPSILON, DBL_EPSILON, LDBL_EPSILON
1.0 与大于 1.0 的最小可表示浮点数之间的差值。
c
#include <stdio.h>
#include <float.h>
int main() {
printf("float epsilon: %e\n", FLT_EPSILON);
printf("double epsilon: %e\n", DBL_EPSILON);
printf("long double epsilon: %Le\n", LDBL_EPSILON);
return 0;
}舍入模式
FLT_ROUNDS
浮点数舍入模式。
c
#include <stdio.h>
#include <float.h>
int main() {
printf("舍入模式: %d\n", FLT_ROUNDS);
switch (FLT_ROUNDS) {
case -1:
printf("不确定\n");
break;
case 0:
printf("向零舍入\n");
break;
case 1:
printf("向最近舍入\n");
break;
case 2:
printf("向正无穷舍入\n");
break;
case 3:
printf("向负无穷舍入\n");
break;
}
return 0;
}实际应用示例
1. 检查浮点数精度
c
#include <stdio.h>
#include <float.h>
#include <math.h>
void test_precision() {
float f = 1.0f;
double d = 1.0;
long double ld = 1.0L;
printf("float 精度测试:\n");
for (int i = 0; i < 10; i++) {
f += 0.1f;
printf("%d: %.15f\n", i + 1, f);
}
printf("\ndouble 精度测试:\n");
for (int i = 0; i < 10; i++) {
d += 0.1;
printf("%d: %.15lf\n", i + 1, d);
}
printf("\nlong double 精度测试:\n");
for (int i = 0; i < 10; i++) {
ld += 0.1L;
printf("%d: %.15Lf\n", i + 1, ld);
}
}
int main() {
test_precision();
return 0;
}2. 浮点数比较
c
#include <stdio.h>
#include <float.h>
#include <math.h>
int float_equal(float a, float b) {
return fabsf(a - b) < FLT_EPSILON;
}
int double_equal(double a, double b) {
return fabs(a - b) < DBL_EPSILON;
}
int main() {
float a = 0.1f + 0.2f;
float b = 0.3f;
printf("a = %.20f\n", a);
printf("b = %.20f\n", b);
printf("a == b: %d\n", a == b);
printf("float_equal(a, b): %d\n", float_equal(a, b));
return 0;
}3. 检查溢出
c
#include <stdio.h>
#include <float.h>
#include <math.h>
void safe_multiply(float a, float b) {
if (a > 0 && b > 0) {
if (a > FLT_MAX / b) {
printf("警告: 乘法可能导致溢出\n");
return;
}
}
float result = a * b;
printf("%.2f * %.2f = %.2f\n", a, b, result);
}
int main() {
safe_multiply(1e30f, 1e30f);
safe_multiply(1.0f, 2.0f);
return 0;
}4. 检查下溢
c
#include <stdio.h>
#include <float.h>
#include <math.h>
void safe_divide(float a, float b) {
if (fabsf(b) < FLT_MIN) {
printf("警告: 除数可能导致下溢\n");
return;
}
float result = a / b;
printf("%.2f / %.2f = %.2f\n", a, b, result);
}
int main() {
safe_divide(1.0f, 1e-30f);
safe_divide(1.0f, 2.0f);
return 0;
}5. 显示浮点数信息
c
#include <stdio.h>
#include <float.h>
void print_float_info() {
printf("=== float 信息 ===\n");
printf("大小: %zu 字节\n", sizeof(float));
printf("尾数位数: %d\n", FLT_MANT_DIG);
printf("十进制精度: %d\n", FLT_DIG);
printf("最小值: %e\n", FLT_MIN);
printf("最大值: %e\n", FLT_MAX);
printf("epsilon: %e\n", FLT_EPSILON);
printf("最小指数: %d\n", FLT_MIN_EXP);
printf("最大指数: %d\n", FLT_MAX_EXP);
printf("\n");
}
void print_double_info() {
printf("=== double 信息 ===\n");
printf("大小: %zu 字节\n", sizeof(double));
printf("尾数位数: %d\n", DBL_MANT_DIG);
printf("十进制精度: %d\n", DBL_DIG);
printf("最小值: %e\n", DBL_MIN);
printf("最大值: %e\n", DBL_MAX);
printf("epsilon: %e\n", DBL_EPSILON);
printf("最小指数: %d\n", DBL_MIN_EXP);
printf("最大指数: %d\n", DBL_MAX_EXP);
printf("\n");
}
void print_long_double_info() {
printf("=== long double 信息 ===\n");
printf("大小: %zu 字节\n", sizeof(long double));
printf("尾数位数: %d\n", LDBL_MANT_DIG);
printf("十进制精度: %d\n", LDBL_DIG);
printf("最小值: %Le\n", LDBL_MIN);
printf("最大值: %Le\n", LDBL_MAX);
printf("epsilon: %Le\n", LDBL_EPSILON);
printf("最小指数: %d\n", LDBL_MIN_EXP);
printf("最大指数: %d\n", LDBL_MAX_EXP);
printf("\n");
}
int main() {
print_float_info();
print_double_info();
print_long_double_info();
return 0;
}6. 选择合适的浮点数类型
c
#include <stdio.h>
#include <float.h>
void choose_float_type(double value) {
if (value >= FLT_MIN && value <= FLT_MAX) {
printf("值 %.2f 可以使用 float 类型\n", value);
} else if (value >= DBL_MIN && value <= DBL_MAX) {
printf("值 %.2f 可以使用 double 类型\n", value);
} else if (value >= LDBL_MIN && value <= LDBL_MAX) {
printf("值 %.2f 可以使用 long double 类型\n", value);
} else {
printf("值 %.2f 超出浮点数范围\n", value);
}
}
int main() {
choose_float_type(1.0);
choose_float_type(1e300);
choose_float_type(1e4000);
return 0;
}7. 累加误差测试
c
#include <stdio.h>
#include <float.h>
void accumulate_error() {
float sum_float = 0.0f;
double sum_double = 0.0;
long double sum_long_double = 0.0L;
float value = 0.1f;
printf("累加误差测试:\n");
printf("float epsilon: %e\n", FLT_EPSILON);
printf("double epsilon: %e\n", DBL_EPSILON);
printf("\n");
for (int i = 0; i < 10; i++) {
sum_float += value;
sum_double += value;
sum_long_double += value;
printf("迭代 %d:\n", i + 1);
printf(" float: %.15f\n", sum_float);
printf(" double: %.15lf\n", sum_double);
printf(" long double: %.15Lf\n", sum_long_double);
printf(" 期望值: %.15f\n", (i + 1) * value);
printf("\n");
}
}
int main() {
accumulate_error();
return 0;
}浮点数注意事项
1. 精度丢失
c
#include <stdio.h>
#include <float.h>
int main() {
float f = 1.234567890123456789f;
double d = 1.234567890123456789;
printf("float: %.20f\n", f);
printf("double: %.20lf\n", d);
return 0;
}2. 比较浮点数
c
#include <stdio.h>
#include <float.h>
#include <math.h>
int main() {
float a = 0.1f + 0.2f;
float b = 0.3f;
printf("a == b: %d\n", a == b);
printf("fabsf(a - b) < FLT_EPSILON: %d\n",
fabsf(a - b) < FLT_EPSILON);
return 0;
}3. 避免累积误差
c
#include <stdio.h>
#include <float.h>
int main() {
float sum = 0.0f;
for (int i = 0; i < 1000000; i++) {
sum += 0.1f;
}
printf("累加结果: %.15f\n", sum);
printf("期望结果: %.15f\n", 100000.0f);
printf("误差: %.15f\n", fabsf(sum - 100000.0f));
return 0;
}总结
<float.h> 提供的浮点数参数宏对于编写可靠的数值计算程序非常重要。通过了解这些参数,开发者可以:
- 选择合适的浮点数类型 - 根据精度和范围需求选择 float、double 或 long double
- 避免精度丢失 - 了解浮点数的精度限制,避免不精确的计算
- 正确比较浮点数 - 使用 epsilon 进行浮点数比较,而不是直接使用 == 运算符
- 检测溢出和下溢 - 在计算前检查数值范围,避免溢出和下溢
- 理解浮点数行为 - 了解舍入模式、精度限制等特性
记住:
- 浮点数计算可能存在精度误差
- 使用 epsilon 进行浮点数比较
- 根据需求选择合适的浮点数类型
- 注意累积误差的影响