C语言中pow函数出现数字错误

3
我想知道数字错误发生在哪一层,这里举个例子来解释:
int p = pow(5, 3);
printf("%d", p);

我在不同的硬件和编译器 (VS 和 GCC) 上测试了这段代码,有些打印出124,有些打印出125。

  • 在相同的硬件(操作系统)上,在不同的编译器(VS 和 GCC)中得到不同的结果。
  • 在不同的硬件(操作系统)上,在相同的编译器(cc (GCC) 4.8.1)中得到不同的结果。

据我所知,pow 得出的结果是 124.99999999,因此被截断为 int 类型,但是这个错误是在哪里发生的?换句话说,更正是在哪里发生的 (124.99->125)?

这是编译器-硬件交互引起的吗?

//****** 编辑:

这里有一段额外的代码片段可供测试 (注意 p=5、p=18 等):

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

int main(void) {
    int p;
    for (p = 1; p < 20; p++) {
        printf("\n%d %d %f %f", (int) pow(p, 3), (int) exp(3 * log(p)), pow(p, 3), exp(3 * log(p)));
    }
    return 0;
}

2
计算机上的浮点数值是一个复杂的问题,它们的操作结果取决于许多因素。例如,硬件支持(如果有),不同软件(编译器、标准库)或硬件使用的舍入算法,以及许多其他因素。 - Some programmer dude
1
当使用常量表达式时,请注意编译器通常会使用内置函数,因此您可能还想尝试使用“-fno-builtin”来查看其是否影响结果。 - Shafik Yaghmour
我相信不是这样的,我刚刚添加了一个使用for循环的代码片段,结果出现了同样的问题。 - igorludi
2
为什么要截断为 int?为什么要使用具有整数指数的 pow 函数?这些才是真正需要问的问题。 - David Heffernan
没错,但正如我所说的 - 我知道这不是最佳做法,我当然也不会提倡这样做。 另外一点,这个问题是由一个学生在尝试计算一个数是否为阿姆斯特朗数时提出的(http://www.cs.mtu.edu/~shene/COURSES/cs201/NOTES/chap04/arms.html)。 对于一个学生来说,这是一个诚实的错误。 - igorludi
1个回答

2
(首先需要注意的是,对于IEEE754双精度浮点类型,所有小于2的53次幂的整数都可以被准确地表示。将浮点精度归咎于整数pow不准确通常是错误的。) pow(x, y)在C中通常实现为exp(y * log(x))。因此,即使是相当小的整数情况下也会出现“偏离”的情况。
对于小的整数情况,我通常手写计算,对于其他整数参数,我使用第三方库。虽然使用for循环自己解决问题很诱人,但是对于整数幂,可以进行有效的优化,这样的解决方案可能无法利用这些优化。
至于观察到的不同结果,可能是由于某些平台使用了80位浮点中间值。也许其中一些计算超过了125,而其他计算则低于该值。

好的,我明白了,并且我知道这不是最佳做法(pow->int)。 我也知道pow会产生舍入误差,但是我不明白这个错误发生在哪里,正如我在问题中所说。 - igorludi
2
对于那些寻找有效的int pow实现的人:https://dev59.com/hnVD5IYBdhLWcg3wE3No - igorludi
1
“log(5)”是一个“double”,它无法被精确表示。exp(3 log (5))很可能不会是一个整数。 - Bathsheba
1
尝试这段代码:#include <stdio.h> #include <math.h>int main(void) { int p; for (p = 1; p < 20; p++) { printf("\n%d %d %f %f", (int) pow(p, 3), (int) exp(3 * log(p)), pow(p, 3), exp(3 * log(p))); } return 0; }使用GCC,我得到的结果是:124 124 125.000000 125.000000使用VS,我得到的结果是:125 124 125.000000 125.000000我不确定pow是否以这种方式实现,或者VS是否对pow进行了更正。 无论如何,仍然让我困惑的是,在另一台机器上,我会得到不同的GCC结果。 这里有各种力量在起作用... - igorludi
我怀疑差异是由于80位中间件引起的。也许C标准库在不同平台上实现pow的方式也有所不同。你可以调整各种编译器设置来改变前者。 - Bathsheba
正如我在上面的评论中指出的那样,由于使用常量表达式的示例可以使用内置函数。 - Shafik Yaghmour

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