C++ 如何提高浮点二阶导数计算的精度?

C++ 如何提高浮点二阶导数计算的精度?,c++,floating-point,floating-accuracy,C++,Floating Point,Floating Accuracy,我编写了一个简单的程序,使用函数指针计算函数的一阶和二阶导数。我的程序计算正确答案(或多或少),但对于某些函数,精度低于我所希望的 这就是我要区分的功能: float f1(float x) { return (x * x); } 这些是使用中心有限差分法的导数函数: // Function for calculating the first derivative. float first_dx(float (*fx)(float), float x) { float h =

我编写了一个简单的程序,使用函数指针计算函数的一阶和二阶导数。我的程序计算正确答案(或多或少),但对于某些函数,精度低于我所希望的

这就是我要区分的功能:

float f1(float x) {
    return (x * x);
}
这些是使用中心有限差分法的导数函数:

// Function for calculating the first derivative.

float first_dx(float (*fx)(float), float x) {
    float h = 0.001;
    float dfdx;

    dfdx = (fx(x + h) - fx(x - h)) / (2 * h);
    return dfdx;
}

// Function for calculating the second derivative.

float second_dx(float (*fx)(float), float x) {
    float h = 0.001;
    float d2fdx2;

    d2fdx2 = (fx(x - h) - 2 * fx(x) + fx(x + h)) / (h * h);
    return d2fdx2;
}
主要功能:

int main() {
    pc.baud(9600);
    float x = 2.0;

    pc.printf("**** Function Pointers ****\r\n");
    pc.printf("Value of f(%f): %f\r\n", x, f1(x));
    pc.printf("First derivative: %f\r\n", first_dx(f1, x));
    pc.printf("Second derivative: %f\r\n\r\n", second_dx(f1, x));
}
这是程序的输出:

**** Function Pointers ****
Value of f(2.000000): 4.000000
First derivative: 3.999948
Second derivative: 1.430511
我对一阶导数的准确性很满意,但我认为二阶导数太远了(应该等于~2.0)

我对浮点数是如何表示的以及为什么它们有时不准确有一个基本的了解,但是我怎样才能使这个二阶导数的结果更准确呢?我是否可以使用比中心有限差分法更好的方法,或者有没有办法用当前的方法得到更好的结果?

  • 进行分析。;-)可能不是一个“当前”选项 方法”
  • 使用double代替float
  • 改变ε(h),并以某种方式组合结果。例如,您可以尝试0.00001、0.000001、0.0000001并对其进行平均。事实上,您希望得到的结果具有最小的h,并且不会溢出/下溢。但目前还不清楚如何检测溢出和下溢

选择精度更高的类型可以提高精度
float
当前定义为IEEE-754 32位数字,精度为~
7.225
小数位

您需要的是64位对应项:
double
,精度为~
15.955
小数位


这对于您的计算应该足够了,但是值得一提的是,它提供了一个四精度浮点数(128位)


最后提供了精度为任意小数位数的类型。

为了提高精度,我建议使用
double
而不是
float
。或者甚至
long double
?啊,太完美了!我以前怎么没有想到这一点?现在,一阶导数和二阶导数的输出分别为
4.000000
2.000000
。不管出于什么原因,我认为double和float的大小是一样的。@StackDanny,你为什么不回答这个问题呢?你可能想改变打印语句的精度,增加小数点。请记住,打印精度可能不同于内部精度(通常内部精度更精确)。当我减小h时,二阶导数的精度会降低。例如,
h=10^-4
-->
2.0000000767
h=10^-5
-->
2.0000001655
h=10^-6
-->
2.0010659796
。我本以为它应该变得更准确。你知道为什么会这样吗?这个答案表明,改变
h
不是个好主意
h
必须足够小,以使函数行为在探测间隔内几乎呈线性,但必须足够大,以使函数值的变化达到许多ULP(在有效位的中间着陆可能是好的)。这个答案中给出的样本值都不符合后一个标准。@EricPostChil我理解函数在
x-h
x+h
之间近似线性的第一点。但是你能解释一下第二点吗?我知道ULP是最后一位的单位,有效位通常是某个数字的尾数。但是它们是如何与此相联系的呢?@AkThao:让我们用十进制来说明,假设我们有一个7位数的十进制格式。考虑在2.345检验f(x)=x*的导数。当h=0.001时,我们有f(2.345+.001)=5.503716和f(2.345)=5.499025,因此导数估计为(5.503716-5.499025)/.001=4.691。h=0.00001,f(2.345+.00001)=5.499072,因此导数估计为(5.499072-5.499025)/.00001=4.7。因为我们只有七位数字,而函数只在最后两位发生了变化,所以在导数中只有两位。因此,如果h太小,函数在浮点格式中的变化量就不够了。这对二阶导数来说更糟。h必须足够大,使函数中的变化至少与我们希望的导数中的有效位数相同。