浮点数很奇怪
几乎每个程序员都犯的第一个错误就是假设这段代码按预期工作:
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
-数学上正确的答案。
发生两件事使这不真实:
- 书面的程序永远不会结束。
a
永远不会等于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.1
,0.2
和 0.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!