如何在C ++中避免这个浮点数舍入问题?

时间:2014-06-20 15:22:41

标签: c++ floating-point floating-point-conversion

使用以下代码,我得到结果" 4.31 43099"。

  double f = atof("4.31");
  long ff = f * 10000L;
  std::cout << f << ' ' << ff << '\n';

如果我改变&#34;加倍f&#34;到&#34;浮动f&#34;。我得到了预期的结果&#34; 4.31 43100&#34;。我不确定是否要改变&#34; double&#34;到&#34;漂浮&#34;是一个很好的解决方案是否有任何好的解决方案可以确保我得到&#34; 43100&#34;?

4 个答案:

答案 0 :(得分:3)

您无法消除浮点算术中的错误(尽管通过适当的分析可以计算出错误)。对于临时使用,你可以做的一件事是获得更直观的结果是将内置的float替换为整数转换(这会截断),并进行正常的舍入:

double f = atof("4.31");
long ff = std::round(f * 10000L);
std::cout << f << ' ' << ff << '\n';

这应输出您期望的内容:4.31 43100


使用10000L也没有意义,因为无论你使用什么类型的整数类型,它仍会转换为f的浮点类型进行乘法运算。只需使用std::round(f * 10000.0);

答案 1 :(得分:2)

问题在于,当谈到十进制数时,浮点本质上是不精确的。转换为二进制时,十进制数可以向上或向下舍入,具体取决于最接近的值。

在这种情况下,您只需要确保如果数字向下舍入,则会向上舍入。您可以通过向值添加尽可能少的数量来实现此目的,如果您使用C ++ 11,则使用nextafter function完成:

long ff = std::nextafter(f, 1.1*f) * 10000L;

如果您没有nextafter,则可以使用numeric_limits进行近似。

long ff = (f * (1.0 + std::numeric_limits<double>::epsilon())) * 10000L;

我刚刚看到你的评论,你只使用4位小数,所以这会更简单但不那么健壮:

long ff = (f * 1.0000001) * 10000L;

答案 2 :(得分:0)

标准的C类型 - 我怀疑。

有许多值无法用这些位表示 - 它们实际上需要更多的空间来存储。所以浮点处理器只使用最接近的处理器。

浮动指向数字不能存储您认为可能的所有值 - 只有有限的位数 - 您不能在32位中存储超过40亿个不同的值。这只是第一个限制。

浮点值(以C表示)表示为:sign - one sign bit, power - bits which defines the power of two for the number, significand - the bits that actually make the number

您的实际号码是sign * significand * 2 inpowerof(power - normalization).

Double是1bit的符号,15位的幂(归一化为正,但不是点)和48位代表值;

很多但不足以代表所有的值,特别是当它们不能很容易地表示为两个幂的有限和时:像二进制1010.101101(101)。例如,它不能精确地表示像1/3 = 0.333333(3)这样的值。这是第二个限制。

尝试阅读 - 对浮点运算的优点和缺点的正确理解可能非常方便: http://en.wikipedia.org/wiki/Floating_pointhttp://homepage.cs.uiowa.edu/~atkinson/m170.dir/overton.pdf

答案 3 :(得分:0)

这里有一些困惑的答案!发生的事情是:4.31无法准确表示为单精度或双精度数。事实证明,最接近的可表示的单精度数字略大于4.31,而最接近的可表示的双精度数字略小于4.31。将浮点值赋给整数变量时,它会向零舍入(不是最接近的整数!)。

因此,如果f是单精度,f * 10000L大于43100,那么它会向下舍入到43100.如果f是双精度,f * 10000L小于43100,所以它向下舍入到43099。

上午的评论。建议f * 10000L + 0.5,这是我认为最好的解决方案。