C ++ pow()函数产生任意精确的结果

时间:2014-08-22 13:23:43

标签: c++ ieee-754 pow arbitrary-precision

如果双精度不保证超过16位有效十进制数,那么这个标准C ++程序产生的输出怎么样?此外,对" ans"进行的小额更改操作也是如此。例如++ ans不改变屏幕输出。是答案"计算"在打印结果之前,而不是以IEEE754双精度格式存储到" ans" rightaway?

重述问题 - 为什么这样的前16位数字的垃圾数字会产生2的幂的正确结果?

#include <cstdio>
#include <cmath>
using namespace std;

int main() {
    double ans = pow(2.0, 700.0);
    printf("%.0f\n", ans);
    return 0;
}

5260135901548373507240989882880128665550339802823173859498280903068732154297080822113666536277588451226982968856178217713019432250183803863127814770651880849955223671128444598191663757884322717271293251735781376

已解决:还找到了解释here

感谢所有回复!

4 个答案:

答案 0 :(得分:3)

您仍然只有大约16位有效小数位;这意味着只有输出的前16位左右是正确的;并且所有剩余的数字可能都非常不正确,并且实际上并不存储为双精度数字的一部分。这就是为什么在数字中添加1不会做任何事情 - 这是对实际未记录的无关紧要数字之一的更改。

答案 1 :(得分:2)

实际上,2**700可以在IEEE double中精确表示,因为它有11位指数部分,因此可以精确打印,并且一些编译器生成这样的代码。但2**700 + 1当然不能精确表示,而是围绕2**700

当然,这个技巧只适用于2的幂,并且只能使用最大为1023的整数幂(指数是有符号数)。

答案 2 :(得分:1)

在IEEE double表示中,使用位来编码值以跟踪+或 - 符号,尾数编码1到1 + 2 ^ 52-1 / 2 ^ 52之间的数字,增量为1/2 ^ 52,以及编码2 ^ -1031和2 ^ 1032之间的乘数的11位指数。引用维基百科:

  

在2 ^ 52 = 4,503,599,627,370,496和2 ^ 53 = 9,007,199,254,740,992之间,可表示的数字正好是整数。对于下一个范围,从2 ^ 53到2 ^ 54,一切都乘以2,所以可表示的数字是偶数等等。

因此,当您尝试将2 ^ 700存储到double时,每当尾数递增到下一个可表示的值时,double的精确值将递增2 ^ 648。要“存储”任何不恰好是这些精确值之一的值,您必须妥协并舍入/截断到最接近的可精确表示的值。所以,当你要求打印没有小数位但是所有数字的数字时,编译器可能会告诉你确切的值代表,即使它与你要求它存储的数字不同...对于7 ^ 297什么是四舍五入后存储。但是对于2 ^ 700,则不需要舍入...尾数为1且指数为700就完全对其进行编码。

我的观点是额外的数字不一定是任意垃圾 - 一个好的printf实现应该显示真正的double值的一部分,并且如果数字在存储时未被舍入则可能有意义

答案 3 :(得分:0)

根据维基百科,IEEE754给出15-17个有效小数位数。 我用gcc 4.8.1在我的Windows机器上编译了它,它产生了98654723617344328000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000

对我来说,标志-std = c ++ 11 -O2 -Wall -pedantic,g ++产生

fld qword [ 0x405070 ]
fstp qword [ esp  + 0x4 ]
call 0x403648  <printf>

令人羡慕的是有点超出了我的舒适区域,但看起来它只是加载双倍并将其扔到堆叠上。看起来像@AntonSavin是对的,在舍入后双倍打印到其最佳能力,这恰好是正确的,这就是为什么ans + 1不会改变。