在使用 IEEE-754 算术和 float 的“单一”(二进制 32)格式的 C++ 实现中,此代码打印“count = 3”:
int count = 0;
for (float f = 0; f < .3f; f += .1f)
++count;
std::cout << "count = " << count << ".\n";
但此代码打印“count = 4”:
int count = 0;
for (float f = 0; f < .33f; f += .11f)
++count;
std::cout << "count = " << count << ".\n";
在第一个示例中,源文本 .1f 被转换为 0.100000001490116119384765625,这是 float 中可表示的值,接近于 0.1。源文本 .3f 转换为 0.300000011920928955078125,float 值最接近 0.3。将 .1f 的转换值添加到 f 会产生 0.100000001490116119384765625,然后是 0.20000000298023223876953125,然后是 0.3000000119209289550765325,此时循环为 0.100000000765333。
在第二个示例中,.11f 转换为 0.10999999940395355224609375,.33f 转换为 0.3300000131130218505859375。在这种情况下,将 .11f 的转换值与 f 相加会产生 0.10999999940395355224609375,然后是 0.2199999988079071044921875,然后是 0.329999983310699462890625。注意,由于四舍五入,这个加3次.11f的结果是0.329999983310699462890625,小于.33f(0.3300000131130218505859375),所以f < .33f为真,循环继续下一次迭代。
这类似于以两位十进制格式添加 ⅓,循环边界为三分之三(即 1)。如果我们有 for (f = 0; f < 1; f += ⅓),则源文本中的 ⅓ 必须转换为 .33(两位十进制数)。然后f 将逐步通过 0.33、0.66 和 0.99。循环直到达到 1.32 才会停止。二进制浮点运算中也会出现相同的舍入问题。
当循环中添加的数量相对于大数字来说是一个小数字时,这些舍入问题就更大了。首先,加法会更多,所以舍入误差也会更多,而且可能会累积。其次,由于较大的数字需要较大的指数才能以浮点格式对其进行缩放,因此它们的绝对精度低于较小的数字。这意味着四舍五入必须相对于添加的小数字更大。所以舍入误差的幅度更大。
那么,即使循环最终终止,由于累积误差,每次迭代中f 的值也可能与期望值相差甚远。如果 f 用于循环内的计算,则计算可能未使用所需的值,并可能产生不正确的结果。