当选择一些正整数n和m时,使用函数(int)pow(n,m)是否会出现错误?

5
假设nm是正整数,并且 nm 在整数范围内,那么(int)pow(n,m)会给出错误的答案吗?
我尝试了许多n,其中 m=2,到目前为止还没有得到错误的答案。

如果m为负数,你很容易得到错误的答案(例如2^-2)。 - DavidW
@DavidW 抱歉,我的意思是正整数。 - user1537366
请在问题中提及 @user1537366。 - ameyCU
3
如果int是一个64位整数,double可能无法准确表示INT_MAX。在这种情况下,(int)pow(INT_MAX, 1) != INT_MAX - EOF
是的,它会。例如,当 n = 2 并且 m > 小数尾数中的有效数字时。 - The Paramagnetic Croissant
4个回答

8
C标准对浮点运算的精度没有任何要求。精度是由实现定义的,这意味着必须记录实现细节。然而,实现留下了一个重要的“出路”:(§5.2.4.2.2第6段,强调添加。)
“浮点操作(+,-,*,/)和和中返回浮点结果的库函数以及由,和中的库函数执行的浮点内部表示和字符串表示之间的转换的精度是由实现定义的。实现可能会声明精度是未知的。”
事实上,gcc通过规定精度未知来利用这一点。尽管如此,glibc计算的精度非常好,即使不能保证。
MS libc实现已知偶尔会在整数参数的pow函数中产生1ULP的误差,如果将pow操作的结果简单截断为int,则会导致错误的值。(我在Visual Studio文档中找不到任何有关浮点精度的规定,但我相信下面的SO问题列表提供了我的论据。)
在x86架构上,大多数实现都会尝试实现IEEE 754,因为本地浮点表示符合标准。然而,在2008年修订版之前,IEEE-754仅要求正确舍入 + - * / sqrt 的结果。自修订版以来,它建议一些其他函数返回正确舍入的结果,但所有这些建议都是可选的,很少有数学库实现了所有这些建议。
如果您真的想使用pow来计算整数的幂,建议(而且很容易)使用lround(pow(n, m))而不是(long)(pow(n, m)),这将把结果四舍五入到最接近的整数,而不是过于乐观地依赖误差为正。如果pow在1ULP范围内,那么这应该可以给出正确的整数值,最高可达252(使用IEEE-754双精度浮点数)。在252和253之间,1ULP误差将为0.5,有时会将结果四舍五入到错误的整数。超过253,并非所有整数都可以表示为双精度浮点数。
实际上,SO上充满了由这个特定问题引起的问题。请参见:

和毫无疑问还有很多其他的。


先生,你能看到这个问题吗https://dev59.com/y1gQ5IYBdhLWcg3w0HSW?它涉及pow函数,但目前还没有好的答案。 - Suraj Jain

3
一些实现会以任何情况下都将pow(x,y)评估为exp(y*log(x)),而其他一些实现则针对整数指数使用平方乘幂。

例如,glibc具有“C”实现,以及具有查找表、多项式逼近等特定于平台的实现。许多Sun/BSD派生产品似乎将整数指数处理为特殊情况。
更重要的是,IEEE-754不要求这样做。它也没有规定结果所需的精度。 glibc记录了其最大ulp错误,尽管此页面可能不是最新的。
总之,即使pow(n,m)具有精确的浮点表示,也不要假设存在精确的整数结果

我认为答案比这更复杂。即使pow是通过logexp实现的,这些操作也需要按照IEEE规则“正确舍入”,请参见https://en.wikipedia.org/wiki/IEEE_floating_point#Recommended_operations。问题是,如果这些操作*被正确舍入*,答案是否会出错?当然,您不能假设每个C实现都遵守IEEE规则,但大多数现代实现都会这样做。 - Mark Ransom
@BrettHale但是文档声称“给定两个IEEE双精度机器数字y,x,它计算X的正确舍入(到最近)值^ y。”因此,它不会产生整数基数和指数的错误结果。 - fuz
@BrettHale 但如果整数可以准确地表示为浮点数(所有32位整数都可以表示为IEEE-754double数字),那么最接近的数字是整数本身,从而验证了OP的假设是正确的。 - fuz
再次强调,您还没有向我展示任何一个共同实现的案例,在OP指定的约束条件下会产生错误的结果。 - fuz
@FUZxxl - 证明的责任并不在我身上。我遵循IEEE-754的要求。你的回答声称可以得到精确结果,但没有提及任何标准或实现,更不用说所有实现了。顶部的答案已经指出这在MS实现中并不是这种情况 - 假设这对你来说已经足够普遍了。 - Brett Hale
显示剩余6条评论

0

在一个 int 有 64 位,double 也有 64 位的平台上,这可能不起作用,因为 pow() 的参数和结果的 double 没有足够的尾数来准确表示每个 64 位整数。在一个普通的平台上,其中 int 有 32 位,double 是一个 64 位 IEEE 754 浮点类型,你的假设是正确的。


在一个普通的平台上,int 有32位,double是64位IEEE 754浮点类型,你的假设是正确的。——不,不完全是。 - The Paramagnetic Croissant
2
计算pow的("most common"等)实现是否保证始终返回calculated >= actual? 中间浮点数舍入可能会导致略小于完整整数值,而(int)转换将向下舍入。 - Jongware
@TheParamagneticCroissant 怎么了?请告诉我哪里有问题。 - fuz
1
@FUZxxl:你对标准中那个句子的理解过度了。请看5.2.4.2.2/6:“浮点运算(+-*/)和返回浮点结果的<math.h><complex.h>库函数的精度是由实现定义的。” - rici
1
@FUZxxl:__STDC_IEC_559__保证了附录F中列出的特性,其中包括符合IEEE-754的1985版,而不是2008版。在1985年,只有+、-、*、/和sqrt需要正确舍入,即使在2008年,额外的函数也是可选的。尽管如此,我确实经常使用浮点数。但是当我转换为整数时,我会确保进行四舍五入而不是截断。 - rici
显示剩余5条评论

0

这取决于您的整数和双精度浮点数的大小。

如果使用32位整数和64位IEEE 754双精度浮点数,则可以为所有可能的整数提供正确答案。

IEEE 754二进制64位浮点数(这是大多数现代计算机上双精度浮点数的常规表示)可以精确表示所有整数[0,2 ^ 53]。


1
@TheParamagneticCroissant 给我一个正整数n和m的反例,证明这个结论不成立。我想不出来任何反例,但我愿意接受我的错误。 - Art
抱歉,我想错了情况。我仍然相信有一些例子,我正在寻找。 - The Paramagnetic Croissant
2
这取决于实现。一些糟糕的实现仅通过 exp(log(n)*m) 计算 pow(n, m),无法为许多整数返回正确的结果。 - phuclv
一些例子:https://dev59.com/xlzUa4cB1Zd3GeqP5rPP https://dev59.com/v3PYa4cB1Zd3GeqPh1ko - phuclv

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