【问题标题】:C: Calculated machine epsilon differs from limits.hC:计算出的机器 epsilon 与 limits.h 不同
【发布时间】:2021-05-19 19:13:36
【问题描述】:

我尝试使用下面的程序用一个简单的 C 程序估计机器 epsilon

#include <stdio.h>
#include <limits.h>
#include <float.h>

int main(){
  float f = 1.0f;
  float prev_f = 1.0f;

  while( 1 + f != 1 ){ 
    prev_f = f;
    f /= 2;  
  }
  prev_f *= 2;  

  printf("Calculated epsilon for float is %.10g", prev_f);
  printf("The actual value is %.10f", FLT_EPSILON);
  return 0;
}

我的输出在哪里

Calculated epsilon for float is 2.802596929e-45

The actual value is 0.0000001192

谁能向我解释这种偏差?它是特定于架构的吗?编译器依赖?我是不是做错了什么?

编辑: 问题似乎是由于我在 gcc 中使用 -Ofast 优化造成的。改用 -O 可以解决问题。

【问题讨论】:

  • 您计算了float精度(24 位 ~= 7.2 个十进制数字),而不是 epsilon。
  • 如果我取出不应该存在的prev_f *= 2; 行,我会得到相同的值。但是在您的输出中,我不知道您如何获得第一个输出 - 可能是某些编译器设置,例如 -ffast-math,这会导致结果不准确..
  • 这很奇怪 - 我得到 2.384185791e-07 这是实际值 * 2。您使用的是什么编译器?你在什么架构上?您使用什么编译器选项?
  • 计算出的正确结果示例:godbolt.org/z/Ghf1TP
  • 还有一种替代方法:stackoverflow.com/a/24915880/6865932

标签: c epsilon


【解决方案1】:

首先,删除prev_f *= 2;。由于循环通过将1 + f != 1 失败的值存储在prev_f 中来记住f 的值,因此循环结束时prev_f 的值是导致1+f 不等于1 的最后一个值,即你想要的结果。1

其次,允许 C 实现以比其名义类型更精确的精度评估浮点表达式。看来您的 C 实现正在以无限精度有效地评估1+f != 1(这可以通过在编译时识别1+f != 1 评估为无限精度是真的,如果f != 0,因此优化可以将其更改为f != 0)。因此,循环仅在f 变为零时终止,此时之前的f 是可表示的最小正值,对于float 常用的格式,它是2-149。 (并且您当前的代码将其加倍并打印 2−148。)请注意,(有效的)无限精度仅在评估 1 + f != 1 时出现,而不是在赋值 f /= 2; 中出现。这是因为 C 标准要求实现在执行强制转换和赋值时“丢弃”多余的精度。这给了我们解决这个问题的方法:将1 + f != 1 更改为(float) (1 + f) != 1。这将强制以float 格式进行评估。2

脚注

1 有点像。机器 epsilon 有时被错误地声明为最小值 x,因此评估 1+x 会产生大于 x 的值。但是,它被定义为 1 和下一个更大的可表示值之间的差异,例如 1+?。如果我们让 x 略大于 ½?(例如 ½?(1+?)),那么由于舍入,计算 1+x 会产生 1+?,即使x 小于机器 epsilon。但是,如果上述问题得到修复并且浮点基数为 2,则此代码将找到正确的值,因为它从未测试机器 epsilon 的这些错误候选之一,因此永远找不到。

2 通常,可能会出现双舍入问题:当 C 实现使用超精度来评估表​​达式时,它可能会将理想的数学结果舍入到超精度。当它通过强制转换或赋值强制“丢弃”多余的精度时,它会四舍五入到标称精度。这两种舍入可能导致与仅对标称精度进行一次舍入时不同的结果。但是,在这种特殊情况下,这不是问题。

【讨论】:

  • 感谢您的详细解答。我有几个问题和cmets。首先,问题似乎是由于使用了 gcc 优化选项 -Ofast。我不太明白为什么。禁用它,并使用上面的代码,我得到一个与limits.h 中包含在FLT_EPSILON 中的值完全相同的值。但是,正如您所写, prev_f *= 2 似乎没有做任何事情。删除它最终会产生相同的值,这让我有点困惑。你知道这是为什么吗?
猜你喜欢
  • 1970-01-01
  • 2015-02-15
  • 2021-10-16
  • 1970-01-01
  • 1970-01-01
  • 2016-04-09
  • 1970-01-01
  • 2013-10-09
  • 1970-01-01
相关资源
最近更新 更多