【发布时间】:2014-05-10 05:50:45
【问题描述】:
我在使用 Visual Studio 和英特尔编译器的 Windows 下遇到双精度问题。
#include <stdio.h>
#include <math.h>
int main()
{
double result = 42;
int rounds = 14;
for(int a=1; a<=rounds; a++)
{
result = sqrt(sqrt(result));
printf("Round %2.i is %.17g\n", a, result);
}
for(int a=1; a<=rounds; a++)
{
result = pow(result, 4);
printf("Round %2.i is %.17g\n", a, result);
}
return 0;
}
使用这些代码,我在 Linux 下使用这些编译器设置生成:
linux下的Intel编译器:
icc -O2 -fp-model double test.cpp -o test
linux下的GCC编译器:
g++ -O2 test.cpp -o test
和Windows下的Matlab和Scilab的正确结果:
Round 1 is 2.5457298950218306
Round 2 is 1.2631446315995756
Round 3 is 1.0601401197016873
Round 4 is 1.0147073765340913
Round 5 is 1.0036567375971333
Round 6 is 1.0009129334669549
Round 7 is 1.0002281552726189
Round 8 is 1.0000570339386641
Round 9 is 1.0000142581797196
Round 10 is 1.0000035645258711
Round 11 is 1.0000008911302767
Round 12 is 1.0000002227824947
Round 13 is 1.000000055695619
Round 14 is 1.0000000139239045
Round 1 is 1.000000055695619
Round 2 is 1.0000002227824947
Round 3 is 1.0000008911302765
Round 4 is 1.0000035645258705
Round 5 is 1.0000142581797171
Round 6 is 1.0000570339386543
Round 7 is 1.0002281552725802
Round 8 is 1.0009129334668005
Round 9 is 1.0036567375965142
Round 10 is 1.0147073765315875
Round 11 is 1.0601401196912235
Round 12 is 1.263144631549705
Round 13 is 2.545729894619797
Round 14 is 41.999999973468661
在 linux 下使用这个 Intel 编译器选项
icc -O2 test.cpp -o test
linux下的这个gcc编译器选项
g++ -O2 -ffast-math test.cpp -o test
和所有 Visual Studio 2012 编译器设置我得到这个错误的结果:
Round 1 is 2.5457298950218306
Round 2 is 1.2631446315995756
Round 3 is 1.0601401197016873
Round 4 is 1.0147073765340913
Round 5 is 1.0036567375971333
Round 6 is 1.0009129334669549
Round 7 is 1.0002281552726189
Round 8 is 1.0000570339386641
Round 9 is 1.0000142581797196
Round 10 is 1.0000035645258711
Round 11 is 1.0000008911302767
Round 12 is 1.0000002227824947
Round 13 is 1.000000055695619
Round 14 is 1.0000000139239045
Round 1 is 1.000000055695619
Round 2 is 1.0000002227824947
Round 3 is 1.0000008911302767
Round 4 is 1.0000035645258711
Round 5 is 1.0000142581797198
Round 6 is 1.0000570339386647
Round 7 is 1.0002281552726222
Round 8 is 1.0009129334669684
Round 9 is 1.0036567375971872
Round 10 is 1.0147073765343093
Round 11 is 1.0601401197025984
Round 12 is 1.2631446316039172
Round 13 is 2.5457298950568319
Round 14 is 42.000000002309839
在 Windows 下,以下设置被忽略,我总是得到错误的结果。
windows下的Intel编译器选项:
icl /O2 /fp:double test.cpp -o test.exe
Windows 下的 Visual Studio 编译器选项:
/fp:precise
/fp:strict
/fp:fast
产生了所有相同的“错误数字”......
为什么会出现这个错误,我该如何解决我在所有平台上获得相同工作双精度的问题?
非常感谢和问候
娜塔莉
编辑:
为了进行基准测试,我循环了代码:
#include <stdio.h>
#include <math.h>
int main()
{
double x = 1;
double result = 0;
int rounds = 12;
while (x!=result)
{
x++;
result=x;
for(int a=1; a<=rounds; a++)
{
result = sqrt(sqrt(result));
}
for(int a=1; a<=rounds; a++)
{
result = pow(result, 4);
}
}
printf("The next possible with %2.i rounds is %.0f\n", rounds, result);
return 0;
}
使用工作的 linux 编译器编译: 英特尔编译器:
icc -O2 -fp-model double speedtest3.cpp -o speedtest3
结果:
The next possible with 12 rounds is 3671078
real 0m1.950s
user 0m1.947s
sys 0m0.003s
GCC 编译器:
g++ -O2 speedtest3.cpp -o speedtest3
结果:
The next possible with 12 rounds is 3671078
real 0m3.445s
user 0m3.442s
sys 0m0.004s
带有 pow 的代码:
#include <stdio.h>
#include <math.h>
int main()
{
double x = 1;
double result = 0;
int rounds = 12;
while (x!=result)
{
x++;
result=x;
for(int a=1; a<=rounds; a++)
{
result = pow(result, 0.25);
}
for(int a=1; a<=rounds; a++)
{
result = pow(result, 4);
}
}
printf("The next possible with %2.i rounds is %.0f\n", rounds, result);
return 0;
}
使用相同的选项编译: 结果英特尔编译器:
The next possible with 12 rounds is 3671078
real 0m2.887s
user 0m2.885s
sys 0m0.004s
结果 GCC 编译器:
The next possible with 12 rounds is 3671078
real 0m5.905s
user 0m5.905s
sys 0m0.008s
结果:
sqrt 比 pow 快近 2 倍 intel 编译器比 gcc 编译器快近 2 倍
【问题讨论】:
-
至少对我来说,不清楚您尝试过哪些平台/编译器,哪些失败了,哪些是正确的。
-
42.000000002309839 错误和 41.999999973468661 正确的依据是什么?例如,第一个比第二个稍微接近 42,尽管考虑到误差的指数,它们都可能是合理的。顺便说一句,在任何 C 实现中,
result*=result;result*=result甚至result=result*result*result*result都可能比result=pow(result,4)更快、更准确,而pow(result, 0.25)很可能比sqrt(sqrt(result))更好。 -
感谢您的回答。我希望 Scilab 和 Matlab 的结果在“双精度”(41.999999973468661)下是正确的。
-
据我了解,42.000000002309839 必须更接近正确的结果。这是长双精度吗?我很快就会对不同的方式进行性能测试。平台:Linux:Gentoo Kernel 3.13.6 强化 x86_64、gcc 4.7.3、icc 14.0.2 Windows:Windows 7 Pro 64bit、Visual Studio 2012 最新更新、icc 14.0.3.202
-
使用
result=result*result*result*result我得到一个全新的不同号码:42.000000637677026 但pow(result, 0.25)工作正常。结果是一样的。
标签: c++ visual-studio gcc compiler-construction double-precision