【问题标题】:Machine Epsilon precision discrepancy机器 Epsilon 精度差异
【发布时间】:2015-04-06 02:27:04
【问题描述】:

作为学校作业的一部分,我正在尝试计算 C++ 中双精度和浮点数的机器 epsilon 值。我在 Windows 7 64 位中使用 Cygwin,代码如下:

#include <iostream>

int main() {
    double epsilon = 1;
    while(1 + epsilon > 1)
        epsilon = epsilon / 2;
    epsilon = 2*epsilon;

    std::cout << epsilon << std::endl;

    float epsilon_f = 1;
    while(1 + epsilon_f > 1)
        epsilon_f = epsilon_f / 2;
    epsilon_f = 2*epsilon_f;

    std::cout << epsilon_f << std::endl;

    return 1;
}

当我运行代码时,我收到两个值的 1.0842e-019。我查了一下,双精度值应该是 2.22e-16,浮点值应该是 1.19e-07。当我在 Macbook 上运行 exact 相同的代码时,代码会返回正确的值。什么可能导致我的 Windows 机器出现差异?

【问题讨论】:

  • 尝试将 -mfpmath 更改为 sse 或 387,并使用 -m64 确保两者都处于 64 位模式。 cygwin 可能默认使用与 macbook 不同的东西。还有-fno-fast-math
  • 如果您还在,并且发现我的回答有帮助,请将其标记为已接受 - 这样,其他人以后就不会偶然发现这个问题并认为它仍然需要回答。此外,如果答案没有帮助,请随时询问更多信息。

标签: c++ double precision epsilon


【解决方案1】:

The CPU's floating-point registers typically contain 80 bits,看起来 Cygwin 编译器选择完全在寄存器中执行循环计算(打印结果时仅将结果截断为 32/64 位)。

正如@Potatoswatter 所指出的,这对于编译器来说是完全合法的,并且您的程序实际上表现出未定义的行为,因为它假定存在 精度限制。由于存在未定义的行为,编译器可能会选择将您的程序转换为它想要的任何东西(包括删除所有文件的程序,但幸运的是这不是一个常见的解决方案......)

附:欢迎来到 StackOverflow,感谢您提出的一个问题(如果您阅读了答案中的概念)可能会让您比班上其他任何人了解更多关于处理器架构和编译器的知识! :-)

【讨论】:

  • ... 更重要的是,允许编译器执行此操作。 C 和 C++ 根本不限制中间结果的精度,甚至不限制某些寄存器的精度。因此,不能保证循环终止并且程序具有未定义的行为。一种解决方案是使用volatile epsilon_f
  • ... 但volatile 只是一个半解决方案,它实际上会阻止静态分析器找到无限循环,但不能保证任何单精度计算。
  • @Potatoswatter:我不知道这甚至是一个半解决方案。 coliru.stacked-crooked.com/a/43ccd29b6d973e12
  • @Potatoswatter:我猜volatile 会在计算中引用变量时强制从内存(实际上只包含 32/64 位)重新加载变量,但实际计算可能仍会执行在寄存器中? (所以它现在不再是未定义的,并且保证会终止,但可能会小于预期的值?)
  • @Potatoswatter:我纠正了自己。您可以使用 volatile (尽管我认为不能保证),但需要 volatile 的不是 epsilon;这是试用金额:coliru.stacked-crooked.com/a/b0490ddec84525b7
猜你喜欢
  • 2015-08-31
  • 2020-03-24
  • 2012-07-18
  • 1970-01-01
  • 2018-06-05
  • 1970-01-01
  • 1970-01-01
  • 2011-01-12
  • 2013-10-09
相关资源
最近更新 更多