为什么我的整数数学与std :: pow给出了错误的答案?

时间:2013-04-06 13:38:58

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

考虑以下代码:

#include <iostream>
#include <cmath>

int main() {
    int i = 23;
    int j = 1;
    int base = 10;
    int k = 2;
    i += j * pow(base, k);
    std::cout << i << std::endl;
}

输出“122”而不是“123”。这是g ++ 4.7.2(MinGW,Windows XP)中的错误吗?

5 个答案:

答案 0 :(得分:10)

std::pow()使用浮点数,它没有无限的精度,可能你正在使用的标准库的实现以(差)方式实现pow(),这使得这种无限的缺乏精确度变得相关。

但是,您可以轻松定义适用于整数的自己的版本。在C ++ 11中,您甚至可以使它constexpr(以便在可能的情况下在编译时计算结果):

constexpr int int_pow(int b, int e)
{
    return (e == 0) ? 1 : b * int_pow(b, e - 1);
}

这是live example


尾递归表格(Dan Nissenbaum的积分):

constexpr int int_pow(int b, int e, int res = 1)
{
    return (e == 0) ? res : int_pow(b, e - 1, b * res);
}

答案 1 :(得分:4)

到目前为止,所有其他答案都错过或围绕问题中唯一的问题跳舞:

C ++实现中的pow质量很差。如果不需要,它会返回一个不准确的答案。

获得更好的C ++实现,或者至少替换其中的数学函数。 one pointed to by Pascal Cuoq很好。

答案 2 :(得分:3)

至少不是我的:

$ g++ --version | head -1
g++ (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2)

$ ./a.out 
123

IDEone也在运行版本4.7.2并提供123


来自http://www.cplusplus.com/reference/cmath/pow/

pow()签名
     double pow (      double base,      double exponent );
long double pow ( long double base, long double exponent );
      float pow (       float base,       float exponent );
     double pow (      double base,         int exponent );
long double pow ( long double base,         int exponent );

您应该设置double base = 10.0;double i = 23.0

答案 3 :(得分:3)

你的问题不是gcc中的错误,这绝对是肯定的。它可能是pow实现中的一个错误,但我认为你的问题实际上只是因为你使用的pow给出了一个不精确的浮点结果(因为它的实现类似于{ {1}}和exp(power * log(base));永远不会绝对准确[除非基数是e的力量]。

答案 4 :(得分:3)

如果你只是写

#include <iostream>
#include <cmath>

int main() {
    int i = 23;
    int j = 1;
    int base = 10;
    int k = 2;
    i += j * pow(base, k);
    std::cout << i << std::endl;
}

你认为pow应该指的是什么? C ++标准甚至不保证在包含cmath之后你将在全球范围内拥有pow功能。

请记住,所有重载至少都在std命名空间中。有pow函数采用整数指数,并且有pow函数采用浮点指数。您的C ++实现很可能只在全局范围内声明C pow函数。此函数采用浮点指数。问题是这个函数可能有一些近似和舍入误差。例如,实现该功能的一种可能方式是:

double pow(double base, double power)
{
    return exp(log(base)*power);
}

由于舍入和逼近误差,很可能pow(10.0,2.0)产生类似99.99999999992543453265的东西。结合浮点到整数转换产生小数点前的数字的事实,这解释了你的结果122,因为99 + 3 = 122。

尝试使用pow的重载,它采用整数指数和/或从float到int进行适当的舍入。采用整数指数的重载可能会给出10到2次幂的确切结果。

编辑:

正如您所指出的,尝试使用std :: pow(double,int)重载似乎也会产生略低于100的值。我花时间检查ISO标准和libstdc ++实现以查看从C ++ 11由于解析defect report 550而导致整数指数的重载被删除。启用C ++ 0x / C ++ 11支持实际上删除了libstdc ++实现中的重载,这可以解释为什么你没有看到任何改进。

无论如何,依赖这种函数的准确性可能是一个坏主意,尤其是涉及转换为整数时。如果您期望浮点值是一个整数(如100)然后将其转换为int类型值,则向零的轻微错误显然会产生很大的差异。所以我的建议是编写你自己的pow函数,它采用所有整数或者使用你自己的round函数对double-gt; int转换特别小心,这样零的小错误就不会改变结果。

相关问题