在 Windows10 与 Debian GNU/Linux 10 中生成不同输出的数值过程

Posted

技术标签:

【中文标题】在 Windows10 与 Debian GNU/Linux 10 中生成不同输出的数值过程【英文标题】:Numerical Procedure generating different outputs in Windows10 vs Debian GNU/Linux 10 【发布时间】:2022-01-19 13:20:15 【问题描述】:

我正在使用 Maehly 程序来完善多项式的根,偶然发现了一些有趣的东西: 确切的相同代码给了我两个非常不同的输出,具体取决于编译它的机器。

守则

#include <stdio.h>

#define MAX_ITERATION 1000

double poly(double x)
    double coeff[9]=-61.688, 72.5235, 72.822, -108.519, -5.12949, 39.9139,-7.07373, -3.91823, 1.0;
    double result=coeff[0];
    double buffer;
    
    for(int i=1; i<9;i++)
        buffer=coeff[i];
        for(int j=1;j<=i;j++)
            buffer*=x;
        
        result+=buffer;
    
    return result;


double poly_der(double x)
    double coeff[8]= 72.5235, 72.822, -108.519, -5.12949, 39.9139,-7.07373, -3.91823, 1.0;
    double result=coeff[0];
    double buffer;
    
    for(int i=1; i<8;i++)
        buffer=coeff[i]*(i+1);
        for(int j=1;j<=i;j++)
            buffer*=x;
        
        result+=buffer;
    
    return result;


int main()
    double roots[8]=0.9, -1.1, 1.4, 1.4, -2.0, -2.0, 2.2, 2.2;
    double factor;
    double pol_eval;
    //Implement Maehly-procedure

    for(int i=0; i<MAX_ITERATION;i++) 
        for(int k=0;k<8;k++)
            factor=0;
            for(int j=0;j<k;j++)
                factor+=1/(roots[k]-roots[j]);
            
            pol_eval=poly(roots[k]);
            roots[k]-=pol_eval/(poly_der(roots[k])-(pol_eval*factor));

        
    
   

    for(int i=0;i<8;i++)
        printf("\n%d: x:%0.16f poly:%e \n",i,roots[i],poly(roots[i]));
    

Windows 输出 (Windows10):

0: x:1.0072928773885637 poly:-8.437695e-015 

1: x:-1.0004044550991309 poly:-2.375877e-014 

2: x:1.3770602924650244 poly:-3.552714e-015  

3: x:-2.5000428878301499 poly:0.000000e+000  

4: x:-1.7318124315476966 poly:-1.136868e-013

5: x:3.0001628929552053 poly:9.094947e-013

6: x:2.2341265341600458 poly:-2.273737e-013

7: x:3.0001628929552049 poly:0.000000e+000

Linux 输出(Debian GNU/Linux 10):

0: x:1.0072928773885637 poly:-8.437695e-15

1: x:-1.0004044550991309 poly:-2.375877e-14

2: x:1.3770602924650244 poly:-3.552714e-15

3: x:-2.5000428878301499 poly:0.000000e+00

4: x:-1.7318124315476959 poly:2.842171e-14

5: x:3.0001628929552093 poly:-1.818989e-12

6: x:2.2341265341600458 poly:-2.273737e-13

7: x:1.5318471775081237 poly:0.000000e+00

x 是多项式的抛光根,起始值保存在数组roots[8] 中。

你能帮我解释一下这种行为吗?最重要的是,帮助我了解如何避免将来发生类似的事情?

【问题讨论】:

我不明白为什么你有一个包含 9 个元素的数组使用全部 9 个元素,一个包含 9 个元素的数组只使用 8 个元素,主要是一个 8 个元素的数组。看起来你可以 &应该简化很多。 在 poly_der 函数上是一个错字,应该是 8,已被编辑。代码不是要优化的,是在run ro clean上写的一些解决方案,我太确定可以优化了! 可能不同的浮点精度/实现会导致结果略有不同。根据您的函数/方程的条件数,这可能会产生不同的结果。 对于 FLT_EVAL_METHOD,我在 windows 上得到 2,在 linux 上得到 0,两台机器都使用 gcc(Windows 中的 MinGw)编译 9.3.0 @Indiano 将产生不同的结果,仅供参考,而不是针对 x64。这是因为 Windows ABI 默认情况下允许 x87 用于 x86-32,但不允许 x86-64。由于 x87 的工作方式和它可以添加的舍入,这可能会产生截然不同的结果。 EX with just clang and GCC producing output 【参考方案1】:

我们有2个问题,为什么不同?哪个更好 - 或者可能是第三种方式?

OP reports 不同的 FLT_EVAL_METHOD 值为 2 和 0。这意味着 Windows 版本使用 long double 数学进行中间计算,而 Linux 版本仅使用 double。通常FLT_EVAL_METHOD==2 更正确。

#include <float.h>
printf("%d\n", FLT_EVAL_METHOD);

FP 的弱点是减去 几乎 相同的值。许多公共位的取消导致轻微计算错误变得更加严重。由于各种原因,不同的编译结果可能略有不同,尽管我_expect 相同。 poly() 通过重复计算找到x 的幂,然后添加抵消的项。

在我的系统上,FLT_EVAL_METHOD 是 0。

同时修复 poly()poly_dev()

static const double coeff[9] = -61.688, //
    72.5235, 72.822, -108.519, -5.12949, //
    39.9139, -7.07373, -3.91823, 1.0;

double poly(double x) 
  double result = 0.0;
  for (int i = 9; i-- > 0; ) 
    result = result * x + coeff[i];
  
  return result;


double poly_der(double x) 
  double result = 0.0;
  for (int i = 9; i-- > 1; ) 
    result = result * x + coeff[i]*i;
  
  return result;

仅使用 double 数学的结果可以与使用 long double 数学的较弱计算代码相媲美。

printf("%d: x:% 0.16f poly:% e \n", i, roots[i], poly(roots[i]));
0: x: 1.0072928773885645 poly: 0.000000e+00 
1: x:-1.0004044550991309 poly:-7.105427e-15 
2: x: 1.3770602924650324 poly:-4.973799e-14 
3: x:-2.5000428878301495 poly:-6.394885e-13 
4: x:-1.7318124315476964 poly:-2.842171e-14 
5: x: 1.5318471775081377 poly: 0.000000e+00 
6: x: 2.2341265341600520 poly: 0.000000e+00 
7: x: 3.0001628929552009 poly:-2.771117e-13 

【讨论】:

谢谢你的回答,我有最后一个问题:我的实现中更好的结果实际上是 FLT_EVAL_METHOD = 0,乍一看这并没有太多直观意义,舍入误差是实际上更低,尾数更短。我的问题背后是否有一些原则或“恰好是这样”? @Indiano "在我的实现中,更好的结果实际上是 FLT_EVAL_METHOD = 0" --> 你是如何确定它更好的。我怀疑这个结论。 根 7 和 5 都是 3.00016 @Indiano 我明白了。但这是一个收敛问题,而不仅仅是一个精确问题——我认为这是主要问题。 “给了我两个真正不同的输出”并没有指出这一点。收敛问题是最初的猜测roots[8] 太远了。一般来说,获得一个好的初始根是一个hard问题。 @Indiano 最后,根据不同的初始猜测求解有趣的多项式,如-(x^3 +x + 1),得到我的profile image。

以上是关于在 Windows10 与 Debian GNU/Linux 10 中生成不同输出的数值过程的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Windows 上构建 gnu `libiconv`?

大神教你Debian GNU/Linux 9.7 “Stretch” Live和安装镜像开放下载

How-to Install VMware Tools on Debian Stretch 9 32/64bit Linux+GNU

如何在没有MariaDB的情况下在Debian GNU / Linux 9上安装MySQL(延伸)?

Debian GNU/Linux 9 将切换至GCC6 编译器

Windows10 WSL2 Ubuntu / Debian # 无网络