当n=5时,使用我的编译器和操作系统,为什么pow(n,2)返回24?

31
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main()
{
    int n,i,ele;
    n=5;
    ele=pow(n,2);
    printf("%d",ele);
    return 0;
}

输出是24

我在Code::Blocks中使用GNU/GCC。

发生了什么?

我知道pow函数返回一个double,但25适合int类型,所以为什么这段代码打印出24而不是25?如果n=4; n=6; n=3; n=2;,那么这段代码可以正常工作,但用五则无法正常工作。


1
你可以尝试将 pow 函数的返回值存储在 floatdouble 变量中,然后将其强制转换为 int。看看是否会产生 24 或正确答案 25 - 0xF1
7
“pow”函数并不仅仅是简单地计算5*5的乘积。最终结果可能为“24.9999999”或类似的结果。由于需要处理分数次幂,“pow”函数可能会使用对数来计算结果。如要确认,请查看您编译器的“pow”函数实现。 - PaulMcKenzie
1
你应该明确说明你使用的操作系统,因为这几乎肯定是标准库数学部分实现中的一个 bug。我猜你在 Windows 上使用 mingw 和 MSVCRT... - R.. GitHub STOP HELPING ICE
1
你能分享一下在 n=5 的情况下执行 printf("%.25lf\n", pow(n,2)); 的输出结果吗? - Mohit Jain
3
一个好的pow(n,2)应该返回准确的结果。 C语言并没有明确指定pow()函数需要有多好。 - chux - Reinstate Monica
显示剩余6条评论
4个回答

32

可能发生的情况如下。您可以通过查看编译器实现的pow函数来确认:

假设您有正确的#include文件,(所有关于此问题的以前的答案和评论都是正确的 - 不要把#include文件视为理所当然),标准pow函数的原型如下:

double pow(double, double);

您正在这样调用pow

pow(5,2);

pow函数通过算法(可能使用对数)进行计算,因此使用浮点函数和值来计算幂值。

由于必须使用分数指数计算pow,因此pow函数不会通过朴素的“将x的值总共乘以n次”来计算,并且无法以这种方式计算分数幂。

因此,很可能使用参数5和2计算pow会导致轻微的舍入误差。 当您赋给int时,您截断了分数值,因此得到24。

如果您正在使用整数,那么最好编写自己的“intpow”或类似函数,只需将该值乘以所需的次数即可。 这样做的好处有:

  1. 不会出现使用pow时可能出现微妙的舍入误差的情况。

  2. 与等效的对pow的调用相比,您的intpow函数很可能运行得更快。


16
如果你要自己实现整数次幂,你应该使用平方法求幂,而不是重复乘法,因为后者的时间复杂度为**O(n),而前者的时间复杂度为O(log n)**。 - aruisdante
3
@aruisdante:小心,符号表明从线性复杂度转变为对数复杂度,但实际上是从伪线性到线性复杂度的转变。 - Ben Voigt
3
@BenVoigt 技术上讲,前面的评论应该更具体:天真算法需要O(n)个算术运算(乘法或加法),其中n是指数;平方将其减少到O(log(n))。如果我们将n中的位数作为问题规模,则可能还希望计算每个乘积中的位数,并且不认为乘法是常量时间操作。对于进行数字计算的人们的实际关注点,我认为维基百科页面过于迂腐;对于复杂性理论的关注点,它的正确性是值得怀疑的。 - David K

2
您希望从一个针对double类型的函数中获得int类型的结果。
您可以尝试使用
ele=(int)(0.5 + pow(n,2));
/*    ^    ^              */
/* casting and rounding   */

最好使用 ele = round(0.5 + pow(n,2));。尽管在这种情况下 pow(n,2) 不应返回小于零的结果,但对于负数的 x 而言,y = (int)(0.5 +x) 是个问题。 - chux - Reinstate Monica
10
不是更好!你应该只使用 round(pow(n,2))。在四舍五入之前添加0.5会有效地将其舍入到下一个整数。如果pow(n,2)返回25加上epsilon,那么你会得到26。 - chqrlie

2

浮点运算不精确。

虽然小的值可以精确地加减,但pow()函数通常通过乘对数来工作,因此即使输入都是精确的,结果也不是精确的。将其分配给int总是截断的,因此如果不精确性为负,则将得到24而不是25。

这个故事的寓意是在整数上使用整数操作,并对<math.h>函数在实际参数需要提升或截断时持怀疑态度。不幸的是,GCC没有警告,除非您添加-Wfloat-conversion(它不在-Wall -Wextra中,可能是因为有许多情况下这种转换是预期和想要的)。

对于整数幂,始终使用乘法(如果为负则使用除法)比pow()更安全、更快-后者保留在需要时使用!但请注意溢出的风险。


1
当你使用变量进行pow计算时,其结果为double。将其赋值给int会截断它。
因此,您可以通过将pow的结果分配给doublefloat变量来避免此错误。
基本上,它翻译成exp(log(x) * y),这将产生一个不完全相同的结果作为浮点值的近似值。例如,5^2将变为24.999999625.00002

我在一次测试中问助教关于在测试中使用int和double的问题,他告诉我可以使用int。但现在我发现他是错的。 - exsnake
1
你的助教需要意识到,你不应该调用那些专为“double”类型设计的函数,并假设它会使用基于“integer”的实现。 - PaulMcKenzie

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