求解数学表达式时 C 中的浮点问题

Floating point issue in C when solving a math expression

我在用 C 语言求解数学函数时遇到了问题。

double task1_double() {
    double a = 1000;
    double b = 0.0001;
    double result = (pow((a + b), 2) - (pow(a, 2) + 2 * a * b))/(pow(b, 2));
    return result; }

float task1_float() {
    float a = 1000;
    float b = 0.0001f;
    float result = (powf((a + b), 2) - (powf(a, 2) + 2 * a * b))/(powf(b, 2));
    return result; }

当我使用双精度数据类型时 returns 1.001172,尽管使用浮点数据类型结果是 6250000.000000。

有人可以解释为什么会这样吗?提前致谢

通过输出中间结果可以发现是float丢精度造成的


#include <stdio.h>
#include <math.h>

double task1_double() {
    double a = 1000;
    double b = 0.0001;
    double s1, s2, s3;
    s1 = pow((a + b), 2);
    s2 = pow(a, 2) + 2 * a * b;
    s3 = pow(b, 2);
    double result = (pow((a + b), 2) - (pow(a, 2) + 2 * a * b)) / (pow(b, 2));

    printf("_double : %lf - %lf = %.15lf\n", s1, s2, s1 - s2);
    printf("_double : (%lf - %lf) / %.10lf = %lf\n", s1, s2, s3, result);

    return result;
}

float task1_float() {
    float a = 1000;
    float b = 0.0001f;
    float s1, s2, s3;
    s1 = powf((a + b), 2);
    s2 = powf(a, 2) + 2 * a * b;
    s3 = powf(b, 2);
    float result = (powf((a + b), 2) - (powf(a, 2) + 2 * a * b)) / (powf(b, 2));

    printf("_float : %lf - %lf = %.15lf\n", s1, s2, s1 - s2);
    printf("_float : (%lf - %lf) / %.10lf = %lf\n", s1, s2, s3, result);

    return result;
}

int main()
{
    printf("%.10lf\n%.10lf\n", task1_double(), task1_float());
    return 0;
}

输出:

_float : 1000000.250000 - 1000000.187500 = 0.062500000000000
_float : (1000000.250000 - 1000000.187500) / 0.0000000100 = 6250000.500000
_double : 1000000.200000 - 1000000.200000 = 0.000000010011718
_double : (1000000.200000 - 1000000.200000) / 0.0000000100 = 1.001172
1.0011717677
6250000.5000000000

可以看出: float运算后出现轻微误差,但除以b的小值会导致误差放大很多倍

你的分子正好是

1000000.20000001 - 1000000.2 = 0.00000001

但是当你减去两个几乎相等的大数时,结果中的相对舍入误差可能会爆炸。这就是你所看到的。这是因为 float 数字有大约 7 个十进制数字的精度,而 double 数字有大约 16 个十进制数字。

让我们一步一步来:

                           exact            float        double
x = pow((a + b), 2)        1000000.20000001 1000000.25   1000000.200000009965
y = pow(a, 2) + 2 * a * b  1000000.2        1000000.1875 1000000.199999999953
x - y                      0.00000001       0.0625       0.000000010011717677

对于两个数字相对于它们的量级之间如此小的差异,您通常会得到 float 结果等于 0.0。但在这种情况下,恰好 1000000.20000001 和 1000000.2 位于舍入边界的任一侧,导致前者向上舍入而后者向下舍入。所以他们的区别是六个数量级。