我在Manjaro 64位,最新版本.惠普展馆g6,Codeblocks版本13.12 rev 9501(2013-12-25 18:25:45)gcc 5.2.0 Linux/unicode - 64位.
学生之间就为什么进行了讨论
所以决定写一个关于它的程序,只是为了向他们展示他们可以期待什么样的输出
#include <iostream>
#include <math.h>
#include <fstream>
using namespace std;
int main()
{
long double sn =0, sn2=0; // sn2 is 1/n^2
ofstream myfile;
myfile.open("/home/Projects/c++/test/test.csv");
for (double n =2; n<100000000;n++){
sn += 1/n;
sn2 += 1/pow(n,2);
myfile << "For n = " << n << " Sn = " << sn << " and Sn2 = " << sn2 << endl;
}
myfile.close();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
从n = 9944开始,我得到sn2 = 0.644834,并且永远得到它.我确实期望编译器将数字四舍五入并在某个时候忽略0,但这太早了,不是吗?
那么在什么理论点0开始被忽略?如果你关心一个数字中的所有0,该怎么办?如果长双不做,那又怎样呢?
我知道这似乎是一个愚蠢的问题,但我希望看到更长的数字,因为你可以在长双打中存储大部分的pi.顺便说一下同样的结果也是双倍的.
你写的代码从一个典型的编程错误受到影响:它通过添加总结浮点数的序列更大的第一号码之和较小以后的数字.
这将不可避免地导致加法期间的精度损失,因为在序列中的某一点,总和将变得相对较大,而序列的下一个成员将变得相对较小.将足够小的浮点值添加到足够大的浮点和不会影响总和.一旦到达该点,即使您尝试添加的值不为零,也会看起来"添加"操作被"忽略".
如果您尝试100000000.0f + 1在典型计算机上进行计算,则可以观察到相同的效果:它仍然评估为100000000.这不会发生,因为1不知何故会四舍五入为零.发生这种情况是因为数学上正确的结果100000001被舍入到100000000.为了强制100000000.0f通过添加进行更改,您需要至少添加5(并且结果将被"捕捉" 100000008).
因此,这里的问题并不是编译器"在它变得如此之小时将数字舍入",正如您似乎相信的那样.你的1/pow(n,2)号码可能很好而且足够精确(没有四舍五入到0).这里的问题是,在你的周期的某个迭代中,1/pow(n,2)just 的小的非零值不再影响总和.
虽然调整输出精度确实可以帮助您更好地了解正在发生的事情(如评论中所述),但真正的问题是上面所描述的.
当计算成员大小差异较大的浮点序列的总和时,应该首先添加序列中较小的成员.100000000.0f再次使用我的示例,您可以轻松地看到4.0f + 4.0f + 100000000.0f正确生成100000008,而100000000.0f + 4.0f + 4.0f仍然是100000000.