简体   繁体   English

重复递增浮点数

[英]Repeated incrementation of a floating point number

I'm looking at some legacy code that defines some grid points along the axis f . 我正在查看一些遗留代码,它们定义了沿轴f一些网格点。

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

I'm worried about the repeated addition of 1 / 8 to a , and the loop not running correctly. 我担心重复添加1/8到a ,并且循环没有正确运行。 This is because I don't think you can add floating point values like this and rely on it being exactly 1 when i is 8. 这是因为我认为你不能像这样添加浮点值,并且当i是8时它依赖于它正好是1。

Or is this code OK and I should stop worrying? 或者这段代码是否正常,我应该停止担心? (The code is at least 20 years old apparently and has never caused trouble - although in the original version, double a was declared outside the loop, I'm reading up on why that is.). (代码显然至少已有20年历史,并且从未造成过麻烦 - 尽管在原始版本中,在循环之外声明了double a ,我正在阅读为什么会这样。)。

The code is fine when compiled with a C compiler that provides either exactly IEEE 754 semantics or a close approximation of them (say, FLT_EVAL_METHOD>0, or even arbitrary excess precision for sub-expressions and arbitrary roundings to the nominal precision). 当使用C编译器编译时,代码很好,C编译器提供精确的IEEE 754语义或它们的近似值(例如,FLT_EVAL_METHOD> 0,或者甚至是子表达式的任意多余精度和任意舍入到标称精度)。

The problems you fear are: 你担心的问题是:

  1. representation error, where 0.125 is not exactly 1/8, and 表示错误,其中0.125不完全是1/8,和
  2. operation error, where + is not exactly mathematical addition. 操作错误,其中+不是完全数学加法。 None of these happen for this particular program. 对于这个特定的程序,这些都不会发生。

0.125 requires 1 bit of precision to be represented exactly in base 2. This means that the floating-point number used by the program in these conditions is exactly the intended 1/8. 0.125要求1位精度在基数2中精确表示。这意味着程序在这些条件下使用的浮点数恰好是预期的1/8。 Furthermore, it can be added to itself 2 53 times before there is any approximation in the addition. 此外,在添加任何近似值之前,它可以添加到自身2 53次。


This reasoning is not correct for other increment steps. 这种推理对于其他增量步骤是不正确的。 For instance, the variation of your program below leaves one index of the array, f[100] , uninitialized with at least my compiler (which implements strict IEEE 754 semantics): 例如,下面的程序的变化留下了一个数组的索引, 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;
    }
}

When I run it I get at the last lines: 当我运行它时,我得到最后一行:

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

f[100] is never written to because of both representation error and operation error that occur when trying to add 0.01 repeatedly to itself in binary floating-point. f[100]永远不会被写入,因为尝试在二进制浮点中重复添加0.01时会出现表示错误和操作错误。

C standard leaves floating number implemented behavior , a lot of compiler use IEEE 754 standard. C标准叶子浮点数实现了行为 ,很多编译器使用IEEE 754标准。 Your code is not Ok because: 您的代码不正常,因为:

  1. Your code must be compile with IEEE 754 standard or similar standard with the same behavior. 您的代码必须使用IEEE 754标准或具有相同行为的类似标准进行编译。
  2. It's hard to read. 这很难读。 When you read the code the exact behavior is not obvious. 当您阅读代码时,确切的行为并不明显。
  3. This is not the right way to iterate over array in C. 这不是在C中迭代数组的正确方法。

Imagine, someone doesn't know that requirement, and don't compile with IEEE 754 standard. 想象一下,有人不知道这个要求,也不用IEEE 754标准编译。 The behavior could be something completely different. 这种行为可能完全不同。 For example, your code could produce a out of bounds, and this is undefined behavior, your program could crash. 例如,您的代码可能会产生越界,这是未定义的行为,您的程序可能会崩溃。


That said, an example of code that have the same behavior of your code when compiled with IEEE 754 standard: 也就是说,使用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;
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM