2 + 2 = 5(*)
(对于一些浮点精度值 2)
当我们将“浮点”视为提高精度的一种方式时,经常会出现这个问题。然后我们与“浮动”部分发生冲突,这意味着无法保证可以表示哪些数字。
因此,虽然我们可以很容易地表示“1.0、-1.0、0.1、-0.1”,但当我们得到更大的数字时,我们开始看到近似值——或者我们应该看到近似值,除非我们经常通过截断数字来隐藏它们以进行显示。
结果,我们可能认为计算机正在存储“0.003”,但它可能会存储“0.0033333333334”。
如果执行“0.0003 - 0.0002”会发生什么?我们期望 .0001,但实际存储的值可能更像是“0.00033” - “0.00029”,产生“0.000004”,或者最接近的可表示值,可能是 0,也可能是“0.000006”。
对于当前的浮点数学运算,不能保证 (a / b) * b == a。
#include <stdio.h>
// defeat inline optimizations of 'a / b * b' to 'a'
extern double bodge(int base, int divisor) {
return static_cast<double>(base) / static_cast<double>(divisor);
}
int main() {
int errors = 0;
for (int b = 1; b < 100; ++b) {
for (int d = 1; d < 100; ++d) {
// b / d * d ... should == b
double res = bodge(b, d) * static_cast<double>(d);
// but it doesn't always
if (res != static_cast<double>(b))
++errors;
}
}
printf("errors: %d\n", errors);
}
ideone 报告了 599 个实例,其中 (b * d) / d != b 仅使用 1 <= b <= 100 和 1 <= d <= 100 的 10,000 个组合。
FAQ 中描述的解决方案本质上是应用粒度约束——进行测试if (a == b +/- epsilon)
。
另一种方法是通过使用定点精度或使用所需的粒度作为存储的基本单位来完全避免该问题。例如,如果您希望以纳秒精度存储时间,请使用纳秒作为存储单位。
C++11 引入了std::ratio作为不同时间单位之间定点转换的基础。