奇怪的pow(x, y)行为问题

6

在做我的作业时,我注意到了一些非常奇怪的事情,但我却无法弄清楚为什么。

int x = 5;
cout << pow(x, 2);

结果是25。这很好。但如果我按照以下方式编写同样的程序:
int x = 5;
int y = pow(x, 2);
cout << y;

结果是24!

当x为2、3、4、6、7、8时没有问题,但当x为5、10、11、13等时,结果比应该的少1。

if()也是同样的情况。

for (int x = 1; x <= 20 ; x++) {
    if (x * x == pow(x, 2)) 
    cout << x << endl;
}

它输出了数字 1、2、3、4、6、8、12、16。


你能把你的完整代码粘贴过来吗? - Techmonk
写pow(x,2)从来不是一个好主意,对于所有目的而言,x*x更好。一个好的pow实现,正确舍入,将产生您期望的结果(5和25可以准确表示)。然而,编写正确的pow需要大量工作,结果可能会很慢,因此许多实现使用一些近似值,这可能会产生令人惊讶的效果。 - Marc Glisse
2
每次发帖只能提一个问题。关于其他问题,请另开新帖。 - David G
4个回答

10

std::pow() 返回一个浮点数。如果结果为24.99999999,并将其强制转换为int,则会被截断为24

这就是第二个代码示例中所做的。
cout 不会转换为 int,因此在第一个代码示例中输出了正确结果。


如果您要将结果存储在int中,您可能希望编写一个round()函数,而不是使用默认行为(截断):https://dev59.com/rXRB5IYBdhLWcg3w3K4J - Robert Mason
在这种情况下,pow()将返回双精度值。 - Arpit
谢谢!我编辑了我的帖子,加上了另一个问题,如果你能看一下就好了! - SoapyCro
@SoapyCro:请不要在一个问题中添加其他问题。这个网站的存在是为了将来所有遇到相同问题的人提供帮助。当你提问时,应该尝试从这个角度看待它。 - juergen d

6

1
operator<<stdout 上是一个重载函数,它将 double y 视为 double,即 y 不会被截断为 int - Arun

5

pow函数适用于floatdouble类型,不适用于整数。当你将其赋值给一个整数时,由于浮点数据的精度表示存在问题,其值可能会被截断。

我建议阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic,因为其中描述了你所看到的行为的原因。

话虽如此,如果你使用double类型而非int类型,你很可能会看到你期望的结果。


1
不错的链接,但我认为对于楼主的情况来说有点过头了哈哈。 - salezica
2
嗯,是的也不是 - 我认为理解为什么会发生这种情况很重要,因为这将防止OP以后感到困惑,并带来更好的习惯。 - Reed Copsey
为什么这里不适用截断?-- liveworkspace.org/code/zbFCw$10 - David G
@David 不同的 pow() 实现可能会在不同的方向上出错。或者它可能会注意到输入是整数,并返回精确的结果。 - Barmar
@SurajJain 当它们是常量/字面值时,编译器可能会将其优化掉... - Reed Copsey
显示剩余2条评论

0

pow()函数通常在数学库中实现,可能使用目标处理器中的特殊指令,在x86上请参见如何:在x86中使用pow(real, real)。然而,像fyl2xf2xm1这样的指令并不快速,因此整个过程可能需要100个CPU周期。出于性能原因,像gcc这样的编译器提供了“内置”函数,可以在特定情况下提供强度降低以更快地执行计算。当幂N是整数(如您的情况)且较小(如您的情况)时,乘以N次比调用库函数更快。

为了检测幂为整数的情况,数学库提供了重载函数,例如double pow(double,int)。您会发现gcc会进行转换。

double x = std::pow(y,4);

内部使用两个乘法运算,比库调用快得多,并在两个操作数都是整数时给出您期望的精确整数结果

double tmp = y * y;
double x = tmp * tmp;

为了获得这种类型的强度降低,您应该:

  1. 包含 < cmath >

  2. 使用优化选项 -O2 进行编译
  3. 显式调用库中的 pow 函数 std::pow(),以确保您获取的是该版本,而不是 math.h 中的版本

然后,您将匹配 < cmath > 中重载的 pow 函数,它看起来像这样:

inline double pow(double __x, int __i) { return __builtin_powi(__x, __i); }

请注意,此函数是使用__builtin_powi实现的,当幂为小整数时,它知道pow()到乘法的强度降低。

@SurajJain 我无法重现你的结果。请参阅http://stackoverflow.com/help/mcve。这是一个工作示例http://coliru.stacked-crooked.com/view?id=f3fced6502893c79 - amdn
在'C'语言中,pow()函数是通过调用标准库来实现的,其签名为"extern double pow(double, double);"。请注意,它接受浮点数值并返回浮点数值。当您传递一个整数时,编译器将转换为double(不会丢失精度),库例程返回一个double(有精度损失),然后结果被截断为零(小于或等于double的最大整数)。标准库使用对数进行近似计算,因此结果类似于99.9999...,并被截断为99。 - amdn
@SurajJain 请参考 https://dev59.com/D3A85IYBdhLWcg3wF_q0 - amdn
先生,您能看到这个问题吗?https://dev59.com/y1gQ5IYBdhLWcg3w0HSW#42165542 - Suraj Jain
@SurajJain,我在那个问题下添加了一条评论。 - amdn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接