【问题标题】:GCC complier :strange behavior when doing float operation , float value saturating to 65536 where float is of 4 bytesGCC 编译器:进行浮点运算时的奇怪行为,浮点值饱和到 65536,其中浮点数为 4 个字节
【发布时间】:2021-12-13 09:33:00
【问题描述】:

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

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

Result for the above code

我用在线 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;
};

【问题讨论】:

  • sizeof的结果是size_t类型的值,需要用%zu打印。虽然它不应该影响程序的行为,但从技术上讲,它确实具有未定义的行为,因为您在打印大小时使用了不匹配的格式说明符和参数。
  • 另外,您展示了三个不同的程序。两个作为图像(请不要发布文本图像,尤其是所有代码)。这三个程序中的哪一个是你的minimal reproducible example?也请花一些时间阅读the help pages,阅读SO tour,阅读How to Ask,以及this question checklist
  • 不要将源代码或程序输出显示为图像。你的程序输出文本,很容易复制粘贴到问题中。

标签: gcc floating-point precision multiplication saturation-arithmetic


【解决方案1】:

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

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

在循环中,dummy1 最终达到值 65535.99609375。然后,当dummy1dummy2 相加时,用实数算术计算的结果将是 65535.9990000000260770320892333984375。此值无法以float 格式表示,因此将其四舍五入为可在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);
}

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

65535.9921875 65535.99609375 65535.99609375 65536 65536.0078125 65536.0078125 65536.0078125 65536.015625 65536.015625 65536.015625

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-04
    相关资源
    最近更新 更多