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

Why is my integer math with std::pow giving the wrong answer?

本文关键字:pow 出错 错误 答案 std 整数 我的 数数 为什么      更新时间:2023-10-16

考虑以下代码:

#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"。这是一个bug在g++ 4.7.2 (MinGW, Windows XP)?

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);
}

到目前为止,所有其他答案都忽略了这个问题中唯一的问题:

您的c++实现中的pow质量很差。当没有必要时,它返回一个不准确的答案。

获得一个更好的c++实现,或者至少替换其中的数学函数。Pascal Cuoq所指的那个是好的

至少不是我的:

$ 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

如果你只是写

#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函数。这个函数接受一个浮点指数。问题是这个函数可能有一些近似误差和舍入误差。例如,实现该函数的一种可能方法是:

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

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

尝试使用pow重载,它接受整数指数和/或从float到int进行一些适当的舍入。采用整数指数的重载可能会为您提供10的2次方的确切结果。

编辑:

正如您所指出的,尝试使用std::pow(double,int)重载似乎也会产生略小于100的值。我花时间检查了ISO标准和libstdc++实现,看到从c++ 11开始,由于解决了缺陷报告550,采用整数指数的重载已经被删除了。启用c++ 0x/c++ 11支持实际上消除了libstdc++实现中的重载,这可以解释为什么你没有看到任何改进。 无论如何,依赖这样一个函数的准确性可能是一个坏主意,特别是在涉及到整数转换的情况下。如果您期望的浮点值是整数(如100),然后将其转换为int型值,那么向零的轻微错误显然会产生很大的差异。因此,我的建议是编写自己的pow函数,该函数接受所有整数,或者使用自己的round函数特别注意double->int转换,以便向零的轻微误差不会改变结果。

你的问题不是gcc中的bug,这是绝对肯定的。这可能是pow实现中的一个bug,但我认为你的问题真的很简单,你正在使用pow,它给出了一个不精确的浮点结果(因为它被实现为exp(power * log(base));log(base)这样的东西永远不会绝对准确[除非基数是e的幂]。