26

考虑以下代码:

#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)中的错误吗?

4

5 回答 5

12

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

但是,您可以轻松定义自己的整数版本。在 C++11 中,您甚至可以制作它constexpr(以便尽可能在编译时计算结果):

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

这是一个活生生的例子


尾递归形式(归功于Dan Nissenbaum):

constexpr int int_pow(int b, int e, int res = 1)
{
    return (e == 0) ? res : int_pow(b, e - 1, b * res);
}
于 2013-04-06T13:47:51.930 回答
7

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

您的powC++ 实现中的质量很差。它在不需要时返回不准确的答案。

获得更好的 C++ 实现,或者至少替换其中的数学函数。Pascal Cuoq 指出的那个很好。

于 2013-04-06T21:00:21.027 回答
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 重载和/或从浮点数到整数进行一些适当的舍入。采用整数指数的重载可能会为您提供 10 的 2 次方的确切结果。

编辑:

正如您所指出的,尝试使用 std::pow(double,int) 重载似乎也产生了一个略小于 100 的值。我花时间检查了 ISO 标准和 libstdc++ 实现,看看从 C++ 开始在图11中,作为解决缺陷报告550的结果,采用整数指数的过载已被丢弃。启用 C++0x/C++11 支持实际上消除了 libstdc++ 实现中的重载,这可以解释为什么您没有看到任何改进。

无论如何,依赖这种函数的准确性可能是一个坏主意,尤其是在涉及到整数的转换时。如果您期望浮点值是整数(如 100),然后将其转换为 int 类型的值,那么朝向零的轻微错误显然会产生很大的不同。因此,我的建议是编写您自己的 pow 函数,该函数采用所有整数,或者使用您自己的 round 函数特别注意 double->int 转换,以便零的轻微错误不会改变结果。

于 2013-04-06T13:56:58.553 回答
3

您的问题不是 gcc 中的错误,这是绝对肯定的。这可能是 的实现中的一个错误pow,但我认为您的问题实际上只是您正在使用的事实,pow它给出了一个不精确的浮点结果(因为它被实现为类似的东西exp(power * log(base));并且log(base)永远不会是绝对准确的 [除非 base是 e] 的幂。

于 2013-04-06T13:51:33.210 回答
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.


pow()来自http://www.cplusplus.com/reference/cmath/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

于 2013-04-06T13:40:54.087 回答