在 C++ 中使用 floor 函数的舍入误差

use*_*748 2 c++ floating-point precision rounding

有人问我以下代码的输出是什么:

floor((0.7+0.6)*10);
Run Code Online (Sandbox Code Playgroud)

它返回 12。

我知道浮点表示不允许以无限精度表示所有数字,我应该期待一些差异。

我的问题是:

  1. 我怎么知道这段代码返回的是 12,而不是 13?为什么 (0.7+0.6)*10比 13一点,而不是一点?

  2. 我什么时候可以预期地板功能不能正常工作,什么时候可以正常工作?

注意:我不是在问浮动表示的样子或为什么输出不完全是 13。我想知道我应该如何推断 (0.7+0.6)*10 有点小于13。

Pas*_*uoq 5

我怎么知道这段代码返回的是 12,而不是 13?为什么 (0.7+0.6)*10 比 13 少一点,而不是多一点?

假设您的编译平台严格使用 IEEE 754 标准格式和操作。然后,将所有涉及的常量转换为二进制,保留 53 个有效数字,并应用 IEEE 754 中定义的基本运算,通过计算数学结果并在每一步四舍五入到 53 个有效二进制数字。在任何阶段都不需要涉及计算机,但是通过使用 C99 的十六进制浮点格式进行输入和输出,您可以使您的生活更轻松。

我什么时候可以预期地板功能不能正常工作,什么时候可以正常工作?

floor()对所有正面论证都是精确的。它在您的示例中正常工作。令你吃惊的行为并非源于也与floor无关floor。令人惊讶的行为开始于 6/10 和 7/10 不能完全表示为二进制浮点值的事实,并且继续是由于这些值具有长扩展、浮点运算+并且*可以产生稍微舍入的结果的事实写出你可以从它们实际应用的参数中得到的数学结果。floor()是代码中唯一不涉及近似的地方。

示例程序以查看发生了什么:

#include <stdio.h>
#include <math.h>

int main(void) {
  printf("%a\n%a\n%a\n%a\n%a\n",
         0.7,
         0.6,
         0.7 + 0.6,
         (0.7+0.6)*10,
         floor((0.7+0.6)*10));
}
Run Code Online (Sandbox Code Playgroud)

结果:

0x1.6666666666666p-1
0x1.3333333333333p-1
0x1.4cccccccccccp+0
0x1.9ffffffffffffp+3
0x1.8p+3

IEEE 754 双精度实际上是针对二进制定义的,但为了简洁起见,有效数以十六进制表示。后面的指数p代表二的幂。例如,最后两个结果都是 <number 大约在 1 和 2 之间的中间>*2 3 的形式

0x1.8p+3是 12。下一个整数 13 是0x1.ap+3,但计算并未完全达到该值,因此 的行为floor()是向下舍入到 12。