首页 > 解决方案 > GCC 编译器:进行浮点运算时的奇怪行为,浮点值饱和到 65536,其中浮点为 4 个字节

问题描述

[我试图计算一个浮点乘法,我观察到该值已经饱和到 65536 并且没有更新。

问题仅在于以下代码。] 1

上述代码的结果

我用在线 GCC 编译器试过这个问题仍然是一样的。

这与浮点精度有什么关系?编译器是否在运行期间优化了我的浮点精度?

有没有我可以添加的编译器标志来解决这个问题?

谁能指导我如何解决这个问题?

附上代码供参考

#include <stdio.h>

int main()
{
    float dummy1, dummy2;
 unsigned int i =0;
    
    printf("Hello World");
    printf("size of float = %ld\n", sizeof(dummy1));
    
    dummy2 = 0.0;
    dummy1 =65535.5;
    
     dummy2 = 60.00 * 0.00005;
    
    for( i= 0; i< 300; i++)
    {
        dummy1 = dummy1 + dummy2;
        printf("dummy1 = %f   %f\n", dummy1, dummy2);
    }

    return 0;
};

标签: gccfloating-pointprecisionmultiplicationsaturation-arithmetic

解决方案


(此答案假定 IEEE-754 单精度和双精度二进制格式用于floatdouble。)

60.00 * 0.00005double算术计算并产生 0.003000000000000000062450045135165055398829281330108642578125。当它存储在 中时dummy2,它被转换为 0.0030000000260770320892333984375。

在循环中,dummy1最终达到值 65535.99609375。然后,当dummy1dummy2相加时,用实数算术计算的结果将是 65535.9990000000260770320892333984375。这个值在float格式中是不可表示的,所以它被四舍五入到格式中可表示的最接近的值float,这就是+运算符产生的结果。

浮点格式中最接近的可表示值是 65535.99609375 和 65536。由于 65536 更接近 65535.9990000000260770320892333984375,因此它是结果。

在下一次迭代中,添加了 65536 和 0.0030000000260770320892333984375。实数运算结果将为 65536.0030000000260770320892333984375。这在 中也无法表示float。最接近的可表示值是 65536 和 65536.0078125。同样 65536 更接近,所以它是计算结果。

从那时起,循环总是产生 65536 作为结果。

您可以通过使用double算术或dummy1在每次迭代中重新计算而不是在迭代之间累积舍入误差来获得更好的结果:

for (i = 0; i < 300; ++i)
{
    dummy1 = 65535.5 + i * 60. * .00005;
    printf("%.99g\n", dummy1);
}

请注意,因为dummy1是 a float,所以它不具有区分序列的某些连续值所需的精度。例如,上面的输出包括:

65535.9921875
65535.99609375
65535.99609375
65536
65536.0078125
65536.0078125
65536.0078125
65536.015625
65536.015625
65536.015625

推荐阅读