C++ C++;Windows下Visual Studio 2012/Intel编译器出现双精度故障

C++ C++;Windows下Visual Studio 2012/Intel编译器出现双精度故障,c++,visual-studio,gcc,compiler-construction,double-precision,C++,Visual Studio,Gcc,Compiler Construction,Double Precision,我对Windows下Visual Studio和英特尔编译器的双精度有问题 #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\

我对Windows下Visual Studio和英特尔编译器的双精度有问题

#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下的GCC编译器:

icc -O2 -fp-model double test.cpp -o test
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下使用此英特尔编译器选项

icc -O2 test.cpp -o test
g++ -O2 -ffast-math test.cpp -o test
这是linux下的gcc编译器选项

icc -O2 test.cpp -o test
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下的英特尔编译器选项:

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;
}
结果:

The next possible with 12 rounds is 3671078
real    0m1.950s
user    0m1.947s
sys     0m0.003s
The next possible with 12 rounds is 3671078

real    0m3.445s
user    0m3.442s
sys     0m0.004s
GCC编译器:

icc -O2 -fp-model double speedtest3.cpp -o speedtest3
g++ -O2 speedtest3.cpp -o speedtest3
The next possible with 12 rounds is 3671078

real    0m2.887s
user    0m2.885s
sys     0m0.004s
The next possible with 12 rounds is 3671078

real    0m5.905s
user    0m5.905s
sys     0m0.008s
结果:

The next possible with 12 rounds is 3671078
real    0m1.950s
user    0m1.947s
sys     0m0.003s
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;
}
结果GCC编译器:

icc -O2 -fp-model double speedtest3.cpp -o speedtest3
g++ -O2 speedtest3.cpp -o speedtest3
The next possible with 12 rounds is 3671078

real    0m2.887s
user    0m2.885s
sys     0m0.004s
The next possible with 12 rounds is 3671078

real    0m5.905s
user    0m5.905s
sys     0m0.008s
结果:

sqrt比pow快近2倍 英特尔编译器的速度几乎是gcc编译器的2倍。它们都是正确的

当以任何精度处理浮点数学时,不能要求结果是最后一位的任何精确数字。期望得到准确的41.99999973468661和期望得到准确的42.000000000000000一样是错误的

这是一个如何获得不同结果的例子:C++中的双精度浮点运算(通常取决于编译器和平台)。然而,英特尔CPU的浮点硬件在内部使用。在某些情况下,这些值会从FPU中提取出来,并修剪为64位。“在某一点”的含义取决于编译器及其优化器认为什么是合适的或最优的,因此,不同的编译器/设置将导致计算的不同部分进行舍入,从而影响结果的精确位数

即使在不同的计算机上使用相同的二进制程序也可能合法地改变结果,并且仍然是“正确的”。例如,过去有些英特尔处理器根本没有硬件浮点单元(很久以前,最后一个是486SX)。在这些情况下,您的程序将在一个单独的纯软件库上运行计算,该库在80位精度下根本不起任何作用

附录:

我想正确地看待事情。沃尔夫阿尔法说,纽约和旧金山之间的距离约为2577英里。在这个尺度上,你所担心的两个数字之间的差异就像说你自己对距离的估计“错了十分之一英寸”(*)。如果这种差异对您的问题很重要,那么浮点运算是错误的工具


*相对误差为(42.000000002309839-41.99999973468661)/41.999999973468661=6.8669471471949833966026905617771e-10。2577英里=163278720英寸。该测量的等效散度为(163278720英寸*6.8669471471949833966026905617771e-10)=0.1121226。。。英寸。所有的计算都是在Windows 7上的Windows Calculator中完成的,它使用定点运算。

看看单向pow(double,double)的实现。来自不同供应商的不同编译器必然包含不同的实现,从而产生差异

正如所建议的,更好的选择是使用

double h = x*x;
x = h*h; 
您所说的“不正确”更准确,请比较:

delta is 2.3098394308362913e-09   42.00000000230983 - 42
delta is 2.6531338903623691e-08   42 - 41.999999973468661
稍后你想证明什么?重复执行某些数值计算通常比直接计算结果更糟糕;因此,pow(42,1.0/(1024*1024*64))在理论上比在循环中取第四个根更可取,相反的过程也是如此:pow(结果,1024*1024*64),这可能使pow能够使用最小的乘法次数


无论如何,42的第无数次方不能表示为一个机器号,因此它的第无数次方必然会偏离42。您可以使用基本的数值数学来估计错误。

至少对我来说,不清楚您尝试了哪些平台/编译器,哪些失败了,哪些是正确的。42.000000002309839错误和41.99999973468661正确的依据是什么?例如,第一种方法比第二种方法稍微接近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.99999979734661)。就我的理解而言,42.000000002309839必须更接近正确的结果。这是长双精度吗?我很快就用不同的方式进行了测试。平台:Linux:Gentoo内核3.13.6加固x86_64,gcc 4.7.3,icc 14.0.2 Windows:Windows 7 Pro 64位,Visual Studio 2012最新更新,icc 14.0.3.202With
result=result*result*result*result
我得到一个全新的不同数字:42.000000637677026,但
pow(result,0.25)
工作正常。结果是一样的。你的代码double h=x*x工作正常,生成更准确的42.000000002309839。但在工作中,数学专家使用matlab,而我必须使用scilab。但是我可以用C++、Matlab和ScILAB来使代码更快。但我必须确保数值计算结果与matlab/scilab中的结果相同。在家里我可以使用linux,但在工作中我必须使用Windows。谢谢,有趣的一点。有没有办法使用80位硬件并将计算结果减少到64位?在工作中