重复递增浮点数

时间:2017-06-06 16:05:32

标签: c floating-point

我正在查看一些遗留代码,这些代码沿轴f定义了一些网格点。

int main(){
    double f[9];
    int i = 0;
    for (double a = 0; a <= 1; a += 0.125){
        f[i++] = a;
    }
}

我担心重复添加1/8到a,并且循环未正确运行。这是因为我认为你不能像这样添加浮点值,并且当i为8时,它依赖于它正好为1。

或者这段代码好吗,我应该停止担心吗? (代码显然至少已有20年历史,并且从未造成过麻烦 - 尽管在原始版本中,double a在循环之外声明,我正在阅读为什么会这样。)。

2 个答案:

答案 0 :(得分:9)

使用C编译器编译时代码很好,C编译器提供精确的IEEE 754语义或它们的近似值(例如,FLT_EVAL_METHOD&gt; 0,或者甚至是子表达式的任意多余精度和任意舍入到标称精度)

你担心的问题是:

  1. 表示错误,其中0.125不完全是1/8,而
  2. 操作错误,其中+不完全是数学加法。对于这个特定的程序,这些都不会发生。
  3. 0.125要求在基数2中精确表示1位精度。这意味着程序在这些条件下使用的浮点数恰好是预期的1/8。此外,在添加任何近似值之前,它可以加到自身2 53 上。

    这种推理对于其他增量步骤不正确。例如,下面的程序的变化留下了一个数组的索引,f[100],至少是我的编译器(实现严格的IEEE 754语义)未初始化:

    int main(){
        double f[101];
        int i = 0;
        for (double a = 0; a <= 1; a += 0.01){
            printf("%.16e %d\n", a, i);
            f[i++] = a;
        }
    }
    

    当我运行它时,我会到达最后一行:

    ...
    9.8000000000000065e-01 98
    9.9000000000000066e-01 99
    

    f[100]永远不会被写入,因为尝试在二进制浮点中重复添加0.01时会出现表示错误和操作错误。

答案 1 :(得分:-2)

C标准离开浮动数实现行为,很多编译器使用IEEE 754标准。您的代码不正常,因为:

  1. 您的代码必须使用IEEE 754标准或类似标准进行编译,但行为相同。
  2. 很难读懂。当您阅读代码时,确切的行为并不明显。
  3. 这不是在C中迭代数组的正确方法。
  4. 想象一下,有人不知道这个要求,也不用IEEE 754标准进行编译。这种行为可能完全不同。例如,您的代码可能会产生越界,这是未定义的行为,您的程序可能会崩溃。

    也就是说,使用IEEE 754标准编译时代码行为相同的代码示例:

    #include <stddef.h>
    
    int main(void) {
      double f[9];
      size_t const size_f = sizeof f / sizeof *f;
    
      double a = 0;
      for (size_t i = 0; i < size_f; i++) {
        f[i] = a;
        a += 0.125;
      }
    }