C++ 求解迭代二次方程会产生噪声

C++ 求解迭代二次方程会产生噪声,c++,C++,我试图解一个二次方程的形式: $x^2+8.888e-5X-(2.777e-6*t+9.34694e-10)=0美元 其中“t”是一个基于迭代的变量。这类似于指数衰减函数。我尝试在C++中实现同样的代码(附上代码)。我可以看到行为/趋势,但是,在最后,当衰减相对于“t”变小时,我看到解决方案中有一些噪声“摆动” 我教过我可以归咎于编译器(5.4.0)的精度,但我的朋友认为使用长双数据类型(e-10)值应该是精确的 附件是我下面的Test.C(例如使用t_end作为0.03,使用numTimeSt

我试图解一个二次方程的形式:

$x^2+8.888e-5X-(2.777e-6*t+9.34694e-10)=0美元

其中“t”是一个基于迭代的变量。这类似于指数衰减函数。我尝试在C++中实现同样的代码(附上代码)。我可以看到行为/趋势,但是,在最后,当衰减相对于“t”变小时,我看到解决方案中有一些噪声“摆动”

我教过我可以归咎于编译器(5.4.0)的精度,但我的朋友认为使用长双数据类型(e-10)值应该是精确的

附件是我下面的Test.C(例如使用t_end作为0.03,使用numTimeSteps作为4500)和我看到的wiggles。请使用“编辑”文件用gnuplot打印。有人能告诉我如何克服这种噪音吗

测试C

#include <iostream>
#include <cmath>
#include <fstream>
#include <stdlib.h>

#define PI 3.141592

using namespace std;

int main ()
{
    long double t_start = 0.;               /*init time*/
    long double t_end;                      /*end time: user  specified*/
    int numTimeSteps;                       /*user specified*/
    long double dt;                         /*(t_end-t_start)/numTimeSteps*/
    cout << "Enter the end time, number of time steps between t_start and t_end" << endl;
    cin >> t_end >> numTimeSteps;
    dt = (t_end - t_start)/numTimeSteps;
    long double time[numTimeSteps]; 
    long double LHS[numTimeSteps];

    /*Terms of quadratic equation: ax^2 + bx + c =0
     * a and b are constants for test case */
    long double a = 1;
    long double b = 8.888e-5;

    long double determinant[numTimeSteps]; 

    long double root1[numTimeSteps], root2[numTimeSteps];
    long double xm[numTimeSteps];

    for (int i = 0; i <= numTimeSteps; i++)
    {
        time[i] = (t_start)+ (i*dt);
        /*Find LHS of equation based on time and other constants*/
        LHS[i] = -(2.777e-6*time[i]+9.34694e-10);

        cout << "Step = " << i << "\t" << time[i] << "\t" << LHS[i] << endl;

        /*Finding roots of Q.Eqn.
         *Find determinant and check for real roots less than 400e-6*/
        determinant[i] = pow(b,2) - 4.*a*LHS[i];
        cout << "Determinant [" << i << "] = " << determinant[i] << endl;

        /*Case1: Roots are real and equal*/
        if (determinant[i] == 0)    
        {
            root1[i] = root2[i] = -b/(2.*a);
            cout << "Determinant is 0: roots are real and equal" << endl;
            cout << root1[i] << endl;
            cout << "\t \t *** \t \t " << endl;
        } 
        /*Case2: Roots are real and different*/
        else if (determinant[i] > 0)
        {
            root1[i] = (-b+sqrt(determinant[i]))/(2.*a);
            root2[i] = (-b-sqrt(determinant[i]))/(2.*a);
            cout << "Determinant is > 0: roots are real and different" << endl;
            cout << "Root1 = " << root1[i] << " Root2 = " << root2[i] << endl;
            cout << "\t \t *** \t \t " << endl;
        }
        else  
            cout << "Determinant is < 0: roots are imaginary" << endl;
    } 

    for (int i = 1; i <= numTimeSteps; i++)
    {
        if (root1[i] > root1[i-1] && root1[i] < 400e-6)
            xm[i] = root1[i];
        else
            xm[i] = 0.;
        cout << "xm = " << xm[i] << endl; 
    }

    ofstream myfile;
    myfile.open("xm");

    if (myfile.is_open())
    {
        for (int i = 1; i <= numTimeSteps; i++)
            myfile << time[i] << "\t" << xm[i] << endl;
        myfile.close();
    }

    return 0;
}

谢谢

这似乎是输出精度的问题。程序生成的
xm
文件包含以下行:

0.0299933 0.00469697
所以大部分的长双尾数已经在那里丢失了。此外,awk脚本还以awk的默认精度打印,这会在以后再次导致相同的问题

放置

myfile.open("xm");
myfile.precision(20); // 20 digits of precision in output
进入C++程序,修改AWK脚本,如:

#!/bin/bash

paste xm| awk '{print $1}' > TimeAllTest
paste xm| awk '{print $2}' > xmTest

# Note: printf instead of print
awk 'NR>1{ printf("%.20f\n", $1-p) }{p=$1}' TimeAllTest > dt
awk '{if (NR!=1) {print}}' TimeAllTest > Time
awk 'NR>1{printf("%.20f\n", $1-p) }{p=$1}' xmTest > dxmTest
paste Time dt dxmTest | awk '{ printf("%.20f %.20f\n", $1, ($3/$2)) }' > tvsuTest
解决了这个问题:


您也可以考虑使用<代码> SqRTL而不是<代码> SqRT(或<代码> STD::Sqrt显式)。我认为现在的情况是,根据gcc 5.4的libstdc++头结构,可以使用旧的

sqrt
C函数,它使用
double
值,而不是
long double
值。这同样适用于
pow
/
powl
/
std::pow
,尽管在那里我真的只使用
b*b

@hnefatl:请停止添加您的方程图片。你的方程式不符合OP发布的内容。@ NicoSchertler Ah yeah,那是我的坏-忘记了<代码> e <代码>文字是C++中的10个基本索引。我将重新编辑以仅包含代码格式并略去等式图像。您的代码将计算
x
作为
t
的函数。如果你区分这个函数(用手很容易做到),你就可以看到“摆动”的存在是否是一个真实的效果——如果是,导数将有零值和变化符号。@彼得摆动是一个伪影(当xm在X轴上的wrt时间变慢时会发生这种情况)。我认为这是由于浮点计算,但编译器应该能够处理(至少因为这个原因我使用长双精度)。我看不出代码有什么奇怪的地方。这是一个符号不变的连续衰减过程。虽然Wintermute已经确定了主要问题,但还有几点:通过减去附近的值来求函数的导数通常是一个坏主意,因为减去两个类似的数字对于浮点精度问题来说是最糟糕的。如果你对“dxm”数据本身感兴趣,而不仅仅是想看看你的“xm”数据有多准确(或不准确),你应该在纸上算出函数的导数,并直接实现这个方程。哦,哇!!我只是在那个点附近打了一下,但不确定如何设置精度。谢谢你!!
#!/bin/bash

paste xm| awk '{print $1}' > TimeAllTest
paste xm| awk '{print $2}' > xmTest

# Note: printf instead of print
awk 'NR>1{ printf("%.20f\n", $1-p) }{p=$1}' TimeAllTest > dt
awk '{if (NR!=1) {print}}' TimeAllTest > Time
awk 'NR>1{printf("%.20f\n", $1-p) }{p=$1}' xmTest > dxmTest
paste Time dt dxmTest | awk '{ printf("%.20f %.20f\n", $1, ($3/$2)) }' > tvsuTest