首页  >  文章  >  后端开发  >  为什么启用编译器优化后,我的浮点舍入代码会产生不同的结果?

为什么启用编译器优化后,我的浮点舍入代码会产生不同的结果?

Susan Sarandon
Susan Sarandon原创
2024-11-14 19:39:02906浏览

Why Does My Floating-Point Rounding Code Produce Different Results with Compiler Optimizations Enabled?

启用优化的浮点舍入差异:编译器错误或优化困境?

浮点计算通常会表现出意外的行为,尤其是在启用编译器优化。考虑以下代码片段:

#include <cstdlib>
#include <iostream>
#include <cmath>

double round(double v, double digit)
{
    double pow = std::pow(10.0, digit);
    double t = v * pow;
    double r = std::floor(t + 0.5);
    return r / pow;
}

int main()
{
    std::cout << round(4.45, 1) << std::endl;
    std::cout << round(4.55, 1) << std::endl;
}

预期输出:

4.5
4.6

但是,当使用带有优化 (O1 - O3) 的 g 编译此代码时,输​​出变为:

4.5
4.5

差异原因:

这种不一致源于 x86 处理器内部使用 80 位扩展精度进行浮点计算。然而,双精度变量通常是 64 位宽。当浮点值从 CPU 寄存器存储到内存时,它们会从 80 位精度舍入到 64 位精度。此舍入可能会引入轻微错误。

优化级别的影响:

不同的优化级别可能会影响浮点值保存到内存中的频率。优化级别越高,这种情况发生的频率就越高。结果,舍入误差变得更加明显。

解决方案:

  1. 使用 -ffloat-store GCC 选项: 这个选项指示编译器将浮点变量存储在内存中而不是寄存器中。这会强制在不同的优化级别上一致地进行舍入。
  2. 使用 long double 类型: long double 在 g 上通常为 80 位宽。使用这种类型可以完全避免舍入问题。
  3. 修改变量存储:将中间计算结果存储到变量中,以最小化舍入误差。

进一步的注意事项:

  • Intel x86_64 版本受此问题的影响较小,因为编译器默认使用 SSE 寄存器来表示 float 和 double,从而无需扩展精度。
  • - mfpmath 编译器选项可用于控制 x86_64 构建中使用的浮点精度。
  • 是否始终打开 -ffloat-store 选项取决于特定应用程序及其对浮点精度的敏感度。对于关键应用程序,使用此选项来确保结果一致可能是明智之举。
  • 调查现有 C 代码和库是否存在潜在问题可能非常耗时。考虑使用工具或实施测试来检测和解决任何浮点精度问题。

以上是为什么启用编译器优化后,我的浮点舍入代码会产生不同的结果?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn