C:计算出的机器 epsilon 与 limit.h 不同

标签 c epsilon

我尝试使用以下程序通过简单的 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 代替可以解决问题。

最佳答案

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

其次,C 实现可以比其标称类型更精确地计算浮点表达式。看来您的 C 实现正在以无限精度有效地评估 1+f != 1 (这可以通过在编译时识别 1+f != 1 评估为无限来实现)当且仅当 f != 0 时精度为 true,因此优化可以将其更改为 f != 0)。因此,循环仅在 f 变为零时终止,此时前一个 f 是最小的可表示正值,对于 常用的格式 float 为2−149。 (您当前的代码将其加倍并打印 2−148。)请注意,(有效)无限精度仅出现在计算 1 + f != 1 时,而不是在计算 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 实现使用超额精度来计算表达式时,它可能会将理想的数学结果舍入到该超额精度。当它通过强制转换或赋值而被迫“丢弃”多余的精度时,它会舍入到标称精度。这两次舍入可能会导致与仅一次舍入到标称精度时不同的结果。但是,在本例中这不会成为问题。

关于C:计算出的机器 epsilon 与 limit.h 不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66232786/

相关文章:

c++ - 给定数字的浮点分辨率

ios - 是否可以获得PID的启动时间?

c - 单个生产者和多个消费者

java - Java 中复杂对象的不精确比较?

c++ - 在比较 float 时使用 epsilon 是否会破坏严格弱排序?

Java 精度测试编号

python - 检测停止进程Python

C - 代码表示头未声明 - 尝试将节点插入链表

C程序编译错误: undefined reference

python - 计算最接近的数字numpy