浮点数很奇怪

几乎每个程序员都犯的第一个错误就是假设这段代码按预期工作:

float total = 0;
for(float a = 0; a != 2; a += 0.01f) {
    total += a;
}

新手程序员假设这将总结 0, 0.01, 0.02, 0.03, ..., 1.97, 1.98, 1.99 范围内的每一个数字,以产生结果 199-数学上正确的答案。

发生两件事使这不真实:

  1. 书面的程序永远不会结束。a 永远不会等于 2,循环永远不会终止。
  2. 如果我们改写循环逻辑来检查 a < 2,则循环终止,但总数最终与 199 不同。在符合 IEEE754 标准的机器上,它通常总结为大约 201

发生这种情况的原因是浮点数表示其指定值的近似值

经典的例子是以下计算:

double a = 0.1;
double b = 0.2;
double c = 0.3;
if(a + b == c)
    //This never prints on IEEE754-compliant machines
    std::cout << "This Computer is Magic!" << std::endl; 
else
    std::cout << "This Computer is pretty normal, all things considered." << std::endl;

虽然我们程序员看到的是用 base10 编写的三个数字,但编译器(和底层硬件)看到的是二进制数。因为 0.10.20.3 需要通过 10 完美划分 - 这在基础 10 系统中非常容易,但在 base-2 系统中是不可能的 - 这些数字必须以不精确的格式存储,类似于 1/3 的数字。在基地 10 中以不精确的形式存储在 0.333333333333333... 中。

//64-bit floats have 53 digits of precision, including the whole-number-part.
double a =     0011111110111001100110011001100110011001100110011001100110011010; //imperfect representation of 0.1
double b =     0011111111001001100110011001100110011001100110011001100110011010; //imperfect representation of 0.2
double c =     0011111111010011001100110011001100110011001100110011001100110011; //imperfect representation of 0.3
double a + b = 0011111111010011001100110011001100110011001100110011001100110100; //Note that this is not quite equal to the "canonical" 0.3!