Skip to content

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> 提供的浮点数参数宏对于编写可靠的数值计算程序非常重要。通过了解这些参数,开发者可以:

  1. 选择合适的浮点数类型 - 根据精度和范围需求选择 float、double 或 long double
  2. 避免精度丢失 - 了解浮点数的精度限制,避免不精确的计算
  3. 正确比较浮点数 - 使用 epsilon 进行浮点数比较,而不是直接使用 == 运算符
  4. 检测溢出和下溢 - 在计算前检查数值范围,避免溢出和下溢
  5. 理解浮点数行为 - 了解舍入模式、精度限制等特性

记住:

  • 浮点数计算可能存在精度误差
  • 使用 epsilon 进行浮点数比较
  • 根据需求选择合适的浮点数类型
  • 注意累积误差的影响