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

时间:2016-02-01 14:06:50

标签: c++ floating-point rounding precision

我被问到以下代码的输出是什么:

floor((0.7+0.6)*10);

它返回12.

我知道浮点表示不允许表示具有无限精度的所有数字,并且我应该预期会有一些差异。

我的问题是:

  1. 我怎么知道这段代码返回12而不是13?为什么(0.7 + 0.6)* 10有点 <13>,而不是更多

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

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

6 个答案:

答案 0 :(得分:4)

  

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

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

  

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

floor()对于所有正面参数都是准确的。它在您的示例中正常工作。令您感到惊讶的行为并非源自floor,而与floor无关。令人惊讶的行为始于6/10和7/10不能完全表示为二进制浮点值的事实,并且继续这样的事实:由于这些值具有长扩展,浮点运算+和{ {1}}可以产生一个略微舍入的结果,因为你可以从它们实际应用的参数中获得数学结果。 *是代码中唯一不涉及逼近的地方。

查看正在发生的事情的示例程序:

floor()

结果:

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

IEEE 754双精度实际上是针对二进制定义的,但为了简明起见,有效数字是用十六进制编写的。 #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)); } 之后的指数表示2的幂。例如,最后两个结果都是形式&lt;数字大致在1和2之间的中间> * 2 3

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

答案 1 :(得分:2)

  
      
  1. 我怎么知道这段代码返回12而不是13?
  2.   

您应该知道可以可能为12或13.您可以通过测试给定的cpu进行验证。

通常,您无法知道该值是什么,因为C ++标准没有指定浮点数的表示。如果您知道给定体系结构的格式(比如IEEE 754),那么您可以手动执行计算,但该结果仅适用于该特定表示。

  

为什么(0.7 + 0.6)* 10小于13,不多一点?

这是一个实现细节,对程序员来说并不是一个有用的知识。所有你需要知道它可能是。依靠它是一个或另一个的知识,会让你依赖于实现细节。

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

它始终正确,这取决于它是如何工作的。

现在,谈谈你期望看到的价值。如果您知道您的数字非常接近整数,但由于表示错误可能会稍微偏离,您可以在地板之前添加0.5

double calculated_integer = (0.7+0.6)*10;
floor(calculated_integer + 0.5);

这样,除非错误超过0.5,否则您将始终获得预期值,这将是一个非常大的错误。

如果您不知道结果应该是整数,那么您只需接受floorceil操作将计算的最大误差增加到{{1}的事实。 }。

答案 2 :(得分:1)

  

我怎么知道这段代码返回12而不是13?

由于这取决于所涉及的数字,通过尝试

  

为什么(0.7 + 0.6)* 10小于13,不多一点?

嗯,因为这是计算的结果。

  

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

正确无误:只有两个幂的倍数,如果您的浮点数用二进制表示。

要真正解决这个问题:

如果不计算结果就无法知道结果;它取决于所涉及的机器/算法和数字。

答案 3 :(得分:1)

有像IEEE floating point standard这样的标准试图使浮点计算至少有一点预测性 通过定义规则如何实现添加和舍入等操作。 要知道结果,需要计算表达式 根据标准规则。那你就可以肯定了 它在每台机器上提供相同的结果,实现标准。

答案 4 :(得分:0)

非常简短的答案:你不能。它取决于平台和此平台上使用的浮点iso。

答案 5 :(得分:0)

一般来说,你不能。根本问题是从文本表示到浮点值的转换通常不能尽可能准确地实现。这部分是动量,部分是因为获得与文本中表达的值最接近的浮点值可能很昂贵,在某些情况下需要大整数计算。因此,转换通常会被理想值中的几个ULP(即低端位)关闭,这是您无法预测先验的方式。因此,代码将产生什么的问题是无法回答的。它应该产生什么的问题可能更容易处理,但它仍然是浪费时间的练习。