Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Introduction

Every elementary function can be differentiated analytically. The definition of the derivative is:

f′(x)=lim⁡Δx→0f(x+Δx)−f(x)Δx.f'(x)=\lim_{\Delta x \rightarrow 0}\frac{f(x+\Delta x)-f(x)}{\Delta x}.

Directly applying the equation above leads to subtracting very similar function values (f(x+Δx)f(x+\Delta x), f(x)f(x)) that are burdened by round-off error, and then dividing them by a small value Δx\Delta x; as a result, the derivative has substantially fewer significant digits than the function values. We avoid numerical differentiation whenever we can; in some cases, however (e.g., solving differential equations), it is an indispensable tool!

In numerical differentiation we have two fundamentally different approaches:

  1. first perform interpolation/approximation, then compute the derivative based on the known interpolation/approximation functions (we already discussed this topic in interpolation and approximation), and

  2. computing the derivative directly from the values in a table.

In this chapter we will learn how to numerically compute the derivative of a function f(x)f(x); here the values of the function f(x)f(x) are given in tabular form (pairs xix_i, yiy_i), as shown in the figure: We will first focus on equidistant values xix_i spaced with a step hh; the function values will be yi=f(xi)y_i=f(x_i).

Based on the definition of the derivative above, the first derivative (at location ii) could be written as:

yi′=yi+1−yih,y_i'=\frac{y_{i+1}-y_{i}}{h},

where h=xi+1−xih=x_{i+1}-x_{i}. By rearranging the equation:

yi′=−yih+yi+1h,y_i'=-\frac{y_{i}}{h}+\frac{y_{i+1}}{h},

we can also say that for the first derivative of the function at location ii we weight the function value at ii by −1/h-1/h and the function value at i+1i+1 by +1/h+1/h.

In what follows we will look at the theoretical background of how to determine the appropriate weights for various orders of derivatives, what options we have here, and how this affects the order of accuracy.

Approximating the first derivative using the finite-difference method

As an introduction, let’s watch the video below:

Loading...

The derivative f′(x)f'(x) can be approximated based on a Taylor series expansion. We call this method the finite-difference method or the difference method.

Let’s expand the forward Taylor series (forward, because of the +h+h term):

f(x+h)=∑n=0∞hnn!dndxnf(x)=f(x)+h f′(x)+h22 f′′(x)+⋯⏟O(h2)f{\left (x + h \right )} =\sum_{n=0}^{\infty}\frac{h^n}{n!}\frac{d^n}{dx^n}f(x)= f{\left (x \right )} + h\, f'\left (x \right ) + \underbrace{\frac{h^2}{2}\,f''(x)+\cdots}_{\mathcal{O}\left(h^{2}\right)}

The term O(h2)\mathcal{O}\left(h^{2}\right) denotes a second-order error. If we express the first derivative from the equation:

f′(x)=1h(f(x+h)−f(x))−h2 f′′(x)+⋯⏟O(h1)f'{\left (x \right )}=\frac{1}{h}\left(f{\left (x + h \right )} - f{\left (x \right )}\right) - \underbrace{\frac{h}{2}\,f''(x)+\cdots}_{\mathcal{O}\left(h^{1}\right)}

We find that we can estimate the first derivative at the point xix_i (that is: fo′(xi)f_o'(x_i)) based on two consecutive function values:

fo′(xi)=1h(yi+1−yi)f_o'(x_i)=\frac{1}{h}\left(y_{i+1}-y_i\right)

and in doing so we make a method error, which is of first order O(h1)\mathcal{O}\left(h^{1}\right).

We used yi=f(xi)y_i=f(x_i) (see the figure above).

The error is:

e=−h2 f′′(ξ),e=-\frac{h}{2}\,f''(\xi),

where ξ\xi is an unknown value on the interval [xi,xi+1][x_i, x_{i+1}] and we neglected the higher-order terms.

The following expression therefore holds:

f′(xi)=fo′(xi)+ef'(x_i)=f_o'(x_i)+e

Now let’s see how we arrive at the same result with a machine-assisted derivation; first we import sympy:

Let’s define the symbols:

Then we continue with the forward Taylor series expansion:

Loading...

The term O(h2)\mathcal{O}\left(h^{2}\right) contains terms of second and higher order. The equation above uses a temporary variable for differentiation ξ1\xi_1; let’s carry out the differentiation and substitute ξ1=x\xi_1=x:

Loading...

We write the equation:

Loading...

We solve it for the first derivative f′(x)f'(x):

Loading...

If we do not account for the second- and higher-order derivatives, we have therefore made an error:

Loading...

The error O(h1)\mathcal{O}\left(h^{1}\right) is therefore of first order, and if we neglect this term we make a method error and obtain an estimate of the derivative:

Loading...

We find that this is the same expression we derived above, so it is:

yi′=1h(−yi+yi+1).y_i'=\frac{1}{h}\left(-y_i+y_{i+1}\right).

The weights are therefore:

Derivative ↓\downarrow \\backslash Values →\rightarrowyiy_{i}yi+1y_{i+1}
yi′=1h⋅y_i'=\frac{1}{h}\cdot-11

Central difference scheme

Derivative f′(x)f'(x)

First let’s look at the backward Taylor series expansion:

Loading...

We find that in the difference between the forward and backward series the even-order terms cancel out; let’s define:

Loading...

We carry out the following steps:

  1. subtract the backward Taylor series from the forward series, the even-order derivatives cancel out,

  2. solve the equation for the first derivative,

  3. determine the method error,

  4. determine the estimate of the derivative.

Let’s carry out the steps above:

The estimate of the 1st derivative is therefore:

Loading...

Or:

yi′=12h(−yi−1+yi+1)y_i'=\frac{1}{2h}\left(-y_{i-1}+y_{i+1}\right)

The weights are therefore:

Derivative ↓\downarrow \\backslash Values →\rightarrowyi−1y_{i-1}yiy_{i}yi+1y_{i+1}
yi′=12h⋅y_i'=\frac{1}{2h}\cdot-101

The method error is therefore of second order:

Loading...

Example: exp⁡(−x)\exp(-x)

Let’s look at an example of the exponential function f(x)=exp⁡(−x)f(x)=\exp(-x) and, for the point x=1.0x=1.0, compute the first derivative f′(x)=−exp⁡(−x)f'(x)=-\exp(-x) at the steps h0=1h_0=1 and h1=0.1h_1=0.1.

First we prepare a table of numerical values and the exact result:

Then we use the forward scheme:

Loading...

Let’s compute the error at x=1.0x=1.0:

Loading...
Loading...

We can confirm that the error at step h/10h/10 is indeed approximately 1/10 of that at step hh.

Now let’s also look at the error for the central difference scheme, which is of second order:

Loading...

Let’s analyze the error:

Loading...
Loading...

We can confirm that the error at step h/10h/10 is indeed approximately 1/100 of that at step hh.

Derivative f′′(x)f''(x)

If we add the forward and backward Taylor series, the odd-order derivatives cancel out:

Loading...

Let’s determine the second derivative:

The estimate of the second derivative is:

Loading...

Or:

yi′′=1h2(yi−1−2 yi+yi+1)y_i''=\frac{1}{h^2}\left(y_{i-1}-2\,y_{i}+y_{i+1}\right)

The weights are therefore:

Derivative ↓\downarrow \\backslash Values →\rightarrowyi−1y_{i-1}yiy_{i}yi+1y_{i+1}
yi′′=1h2⋅y_i''=\frac{1}{h^2}\cdot1-21

The method error is again of second order:

Loading...

Derivative f′′′(x)f'''(x)

If we want to determine the third derivative, we have to expand the Taylor series to order 5:

Loading...

Using the 1st derivative we derived above would not help us, since the order of the error is O(h2)\mathcal{O}\left(h^{2}\right), which means that in the equation above, when dividing by h3h^3, we would get O(h−1)\mathcal{O}\left(h^{-1}\right).

Let’s use a trick: we repeat the expansion, but based on additional points that are 2h2h and −2h-2h away from xx:

Loading...

Now we have two equations and two unknowns; we will solve the system step by step:

  1. solve equation eq_h for the first derivative,

  2. solve equation eq_2h for the first derivative,

  3. equate the results of the first two steps and solve for the third derivative,

  4. determine the method error,

  5. determine the estimate of the derivative.

Let’s carry out the stated steps:

The estimate of the 3rd derivative is:

Loading...

Or:

yi′′′=1h3(−yi−2/2+yi−1−yi+1+yi+2/2)y_i'''=\frac{1}{h^3}\left(-y_{i-2}/2+y_{i-1}-y_{i+1}+y_{i+2}/2\right)

The weights are therefore:

Derivative ↓\downarrow \\backslash Values →\rightarrowyi−2y_{i-2}yi−1y_{i-1}yiy_{i}yi+1y_{i+1}yi+2y_{i+2}
yi′′′=1h3⋅y_i'''=\frac{1}{h^3}\cdot-0.510-10.5

Let’s confirm that the method error is of second order:

Loading...

Derivative f(4)(x)f^{(4)}(x)

Let’s repeat a procedure similar to the one for the 3rd derivative, but for the 4th derivative we add the forward and backward Taylor series (up to order 6):

Loading...

Let’s prepare an additional equation based on the points that are 2h2h and −2h-2h away from xx:

Loading...

From the two equations we determine the 4th derivative:

The estimate of the 4th derivative is:

Loading...

Or:

yi(4)=1h4(yi−2−4 yi−1+6 yi−4 yi+1+yi+2)y_i^{(4)}=\frac{1}{h^4}\left(y_{i-2}-4\,y_{i-1}+6\,y_i-4\,y_{i+1}+y_{i+2}\right)

The weights are therefore:

Derivative ↓\downarrow \\backslash Values →\rightarrowyi−2y_{i-2}yi−1y_{i-1}yiy_{i}yi+1y_{i+1}yi+2y_{i+2}
yi(4)=1h4⋅y_i^{(4)}=\frac{1}{h^4}\cdot1-46-41

Let’s confirm that the method error is of second order:

Loading...

Summary of the central difference scheme

Above we derived the first four derivatives with a 2nd-order method error. The key point of the derivations above is that they give us the weights by which we must multiply the function values in order to compute the approximation of a given derivative. For this reason, we will collect these weights here. If you are impatient, you can jump to the table below. Continue reading if you want to learn how to organize the previously derived expressions by machine.

First let’s collect all the derivative estimates into a list:

Loading...

We have 5 function values available (at locations x−2h,x−h,x,x+h,x+2hx-2h, x-h, x, x+h, x+2h), which we put into a list:

We compute the weight of the first derivative for the function value f(x−h)f(x-h):

Loading...

Now let’s generalize and compute the weights for all function values and for all derivative estimates:

Loading...

The summaries above can also be written in tabular form:

Derivative ↓\downarrow \\backslash Values →\rightarrowyi−2y_{i-2}yi−1y_{i-1}yiy_{i}yi+1y_{i+1}yi+2y_{i+2}
yi′=1h⋅y_i'=\frac{1}{h}\cdot0-0.500.50
yi′′=1h2⋅y_i''=\frac{1}{h^2}\cdot01-210
yi′′′=1h3⋅y_i'''=\frac{1}{h^3}\cdot-0.510-10.5
yi(4)=1h4⋅y_i^{(4)}=\frac{1}{h^4}\cdot1-46-41

The central difference scheme shown has a 2nd-order error O(h2)\mathcal{O}(h^{2}).

Note: if you are interested in a generalization of the difference method, please see the findiff package.

Improved approximation - Richardson extrapolation

If the exact derivative is computed as:

f′(xi)=fo′(xi)+e,f'(x_i)=f_o'(x_i)+e,

where fo′(xi)f_o'(x_i) is the numerically computed derivative at the point xix_i and ee is the error estimate.

For a method of accuracy order nn: O(hn)\mathcal{O}(h^{n}) at step hh the following holds:

f′(xi)=fo′(xi,h)+K hn,f'(x_i)=f_o'(x_i, h)+K\,h^n,

where KK is an unknown constant.

If we halve the step and assume that KK does not change, the following holds:

f′(xi)=fo′(xi,h2)+K (h2)n.f'(x_i)=f_o'\left(x_i, \frac{h}{2}\right)+K\,\left(\frac{h}{2}\right)^n.

From both equations we eliminate the constant KK and determine the improved approximation:

f‾′(xi)=2n fo′(xi,h2)−fo′(xi,h)2n−1\overline{f}'(x_i)=\frac{2^n\,f_o'\left(x_i, \frac{h}{2}\right)-f_o'(x_i, h)}{2^n-1}
Example

Let’s look at the example f(x)=sin⁡(x)f(x)=\sin(x) (the analytical derivative is: f′(x)=cos⁡(x)f'(x)=\cos(x)):

At step hh we have the function values defined at:

array([0. , 0.78539816, 1.57079633, 2.35619449, 3.14159265, 3.92699082, 4.71238898, 5.49778714, 6.28318531])

The numerical derivative at x=πx=\pi:

Loading...

and at step hh is

Loading...

The computation at step 2h2h:

Loading...

Let’s compute the improved estimate for x=πx=\pi:

Loading...

We see that the improved estimate is closest to the theoretical value cos⁡(π)=−1\cos(\pi)=-1.

Non-central difference scheme

The central difference scheme we learned above is very useful and relatively accurate. However, since we cannot always use it (for example, at the beginning or end of the table), we have to rely on non-central difference schemes for computing derivatives.

We have:

  • the forward difference scheme, which approximates the derivative at a point with the function values at the following points, and

  • the backward difference scheme, which approximates the derivative at a point with the values at the preceding points.

The derivations are similar to what we showed for the central difference scheme, so we will not treat them here and will only show the final result.

Forward difference scheme

Forward difference scheme with error order O(h1)\mathcal{O}(h^{1}):

Derivative ↓\downarrow \\backslash Values →\rightarrowyiy_{i}yi+1y_{i+1}yi+2y_{i+2}yi+3y_{i+3}yi+4y_{i+4}
yi′=1h⋅y_i'=\frac{1}{h}\cdot-11000
yi′′=1h2⋅y_i''=\frac{1}{h^2}\cdot1-2100
yi′′′=1h3⋅y_i'''=\frac{1}{h^3}\cdot-13-310
yi(4)=1h4⋅y_i^{(4)}=\frac{1}{h^4}\cdot1-46-41

Forward difference scheme with error order O(h2)\mathcal{O}(h^{2}):

Derivative ↓\downarrow \\backslash Values →\rightarrowyiy_{i}yi+1y_{i+1}yi+2y_{i+2}yi+3y_{i+3}yi+4y_{i+4}yi+5y_{i+5}
yi′=12h⋅y_i'=\frac{1}{2h}\cdot-34-1000
yi′′=1h2⋅y_i''=\frac{1}{h^2}\cdot2-54-100
yi′′′=12h3⋅y_i'''=\frac{1}{2h^3}\cdot-518-2414-30
yi(4)=1h4⋅y_i^{(4)}=\frac{1}{h^4}\cdot3-1426-2411-2

Backward difference scheme

Backward difference scheme with error order O(h1)\mathcal{O}(h^{1}):

Derivative ↓\downarrow \\backslash Values →\rightarrowyi−4y_{i-4}yi−3y_{i-3}yi−2y_{i-2}yi−1y_{i-1}yiy_{i}
yi′=1h⋅y_i'=\frac{1}{h}\cdot000-11
yi′′=1h2⋅y_i''=\frac{1}{h^2}\cdot001-21
yi′′′=1h3⋅y_i'''=\frac{1}{h^3}\cdot0-13-31
yi(4)=1h4⋅y_i^{(4)}=\frac{1}{h^4}\cdot1-46-41

Backward difference scheme with error order O(h2)\mathcal{O}(h^{2}):

Derivative ↓\downarrow \\backslash Values →\rightarrowyi−5y_{i-5}yi−4y_{i-4}yi−3y_{i-3}yi−2y_{i-2}yi−1y_{i-1}yiy_{i}
yi′=12h⋅y_i'=\frac{1}{2h}\cdot0001-43
yi′′=1h2⋅y_i''=\frac{1}{h^2}\cdot00-14-52
yi′′′=12h3⋅y_i'''=\frac{1}{2h^3}\cdot03-1424-185
yi(4)=1h4⋅y_i^{(4)}=\frac{1}{h^4}\cdot-211-2426-143

Using numpy.gradient

To compute numerical derivatives (2nd-order central difference scheme) we can also use numpy.gradient() (documentation):

gradient(f, *varargs, **kwargs)

where f represents a table of values (in the form of a numerical array) of the function whose derivative we are looking for. f can be one or more dimensions. The positional parameters varargs define the spacing between the values of the argument of the function f; the default value is 1. This value can be a scalar, but it can also be a list of values of the independent variable (or even a combination of both). At the edges, the gradient method uses the forward or backward scheme; the parameter edge_order defines the order of the scheme used at the edges (we can choose between 1 or 2, the default value is 1).

The result of the gradient function is a numerical array (or an array of numerical arrays) with the computed derivatives.

For details see the documentation.

For differentiating a function (and not a table of values), see: scipy.differentiate.

Example

We will look at an example of how to use the weights, the gradient function, and the special handling at the edges. First we prepare a table of data:

Weights of the difference schemes:

Now we compute the derivative of the interior points (the first way is with list comprehensions, the second is vectorized):

At the edges we use the forward or backward difference scheme:

Let’s assemble the result:

We display the result together with the result of the np.gradient function:

<Figure size 640x480 with 1 Axes>

Using numpy.diff

Let’s also present here the frequently used function numpy.diff (documentation), which computes the n-th discrete difference along a given axis. The first difference is given as a[i+1]−a[i]a[i+1] - a[i]. This is the basic operation for numerical differentiation (it is a first-order accurate derivative), but we have to divide the result by the step hh.

When using np.diff, the length of the array is reduced by 1.

<Figure size 640x480 with 1 Axes>

Round-off error in numerical differentiation

Above we focused on the method error. In numerical differentiation, however, we must also pay very close attention to the round-off (or representation) error! Let’s look at the first derivative (using the central difference scheme) written with the method error (k h2k\,h^2) and the round-off error ε\varepsilon:

yi′=12h((−yi−1±ε)+(yi+1±ε))+k h2y_i'=\frac{1}{2h}\left((-y_{i-1}\pm\varepsilon)+(y_{i+1}\pm\varepsilon)\right) + k\,h^2

In the worst case the round-off errors add up and the total error is:

n=εh+k h2n=\frac{\varepsilon}{h}+k\,h^2

When hh is large, the method error k h2k\,h^2 dominates; when hh is small, the round-off error dominates. The error has a minimum when:

n′=−εh2+2 k h=0.n'=-\frac{\varepsilon}{h^2}+2\,k\,h=0.

It follows that:

h=ε2 k3.h=\sqrt[3]{\frac{\varepsilon}{2\,k}}.

Example

Below we will look at an example where we vary the precision in three steps:

  1. float16 - 16-bit format: sign 1 bit, exponent 5 bits, mantissa 10 bits

  2. float32 - 32-bit format: sign 1 bit, exponent 8 bits, mantissa 23 bits

  3. float64 - 64-bit format: sign 1 bit, exponent 11 bits, mantissa 52 bits (this is the default precision).

For more about types in numpy see the documentation

Now let’s determine the basic round-off error for each type:

The basic round-off error for the types `float16`, `float32`, and `float64` is:
[np.float16(0.000977), np.float32(1.1920929e-07), np.float64(2.220446049250313e-16)]

As an example let’s look at addition: we add half of the basic round-off error eps16 to the number 1. and convert to the type float16; we find that the new value is still equal to the value 1.:

np.float16(1.0)

First let’s define the function exp⁡(x)\exp(x), which will give a result with the precision defined by the parameter dtype:

Let’s also define a function for the analytically determinable derivative (we will need this later to determine the relative error):

Similarly to addition above, we can also see for the function value that a change in the value xx smaller than ϵ\epsilon leads to the same result:

np.float16(0.368)
np.float16(0.368)

Now let’s use the central difference scheme for the first derivative. Here the numbers should be written with precision dtype, and with the help of the exact derivative the relative error is also computed:

Let’s look at an example of the derivative at x=1.0x=1.0 (the Python function returns the value and the relative error):

(np.float16(-0.3662), np.float64(0.004535463210798897))
Loading...

Now let’s define the step:

array([1.00000000e+00, 2.50000000e-01, 6.25000000e-02, 1.56250000e-02, 3.90625000e-03, 9.76562500e-04, 2.44140625e-04, 6.10351562e-05, 1.52587891e-05, 3.81469727e-06])

We compute the derivative estimate for various precisions (because of division by 0 we get a warning):

/tmp/ipykernel_355507/3908051182.py:2: RuntimeWarning: invalid value encountered in divide
  f1_estimate = (fun(x+h, dtype=dtype)-fun(x-h,dtype=dtype))/(2*dtype(h))

We plot the various types as a function of the step size hh. First we import the required libraries:

Let’s define the figure:

Let’s display it:

<Figure size 640x480 with 1 Axes>

At a relatively large step hh the method error dominates, while at a small step the round-off error dominates; the optimal step can be estimated based on:

h=ε2 k3.h=\sqrt[3]{\frac{\varepsilon}{2\,k}}.

In this particular case:

k=−f′′′(x)6=−−e−x6=16 e(x=1)k=-\frac{f'''(x)}{6}=-\frac{-e^{-x}}{6}=\frac{1}{6\,e}\qquad(x=1)

It follows that:

h=3 ε e3.h=\sqrt[3]{3\,\varepsilon\,e}.

We compute a suitable step for 16-, 32-, and 64-bit format:

Loading...
Loading...
Loading...

The 64-bit format performs best, but even with it a step smaller than about 1e-5 is not recommended!

Complex-step derivative approximation

The complex-step derivative approximation is useful when we have any analytic function defined but do not have an analytical expression for the first derivative; for details see (source). The idea stems from the Taylor series expansion of the function f(x)f(x), but the step is taken in the direction of the imaginary axis i hi\,h (i=(−1)i=\sqrt(-1)):

f(x+i h)=f(x)+i h ddxf(x)−h22d2dx2f(x)+O(h3)f{\left(x + i\,h \right)} = f{\left(x \right)} + i\,h\,\frac{d}{d x} f{\left(x \right)} - \frac{h^{2}}{2} \frac{d^{2}}{d x^{2}} f{\left(x \right)} + O\left(h^{3}\right)

If we now assume that the function f(x)f(x) maps real values to the real axis and that xx and hh are real values, then we can derive:

Im(f(x+i h))=h ddxf(x)+O(h3)\mathbf{Im} \left( f{\left(x + i\,h \right)}\right)= h\,\frac{d}{d x} f{\left(x \right)}+ O\left(h^{3}\right)
Re(f(x+i h))=f(x)−h22d2dx2f(x)+O(h3)→Re(f(x+i h))=f(x)+O(h2)\mathbf{Re} \left( f{\left(x + i\,h \right)}\right) = f{\left(x \right)} - \frac{h^{2}}{2} \frac{d^{2}}{d x^{2}} f{\left(x \right)} + O\left(h^{3}\right)\rightarrow \mathbf{Re} \left( f{\left(x + i\,h \right)}\right) = f{\left(x \right)} + O\left(h^{2}\right)

From the first equation (above) we then derive the expression for the first derivative:

f′(x)=Im(f(x+i h))h+O(h2)f'{\left(x \right)}=\frac{\mathbf{Im} \left( f{\left(x + i\,h \right)}\right)}{h}+ O\left(h^{2}\right)

The result is in a way very surprising. Above, with a one-step forward scheme, we managed to obtain accuracy O(h1)O\left(h^{1}\right), while here it is O(h2)O\left(h^{2}\right)!

An important advantage of the complex-step method is that it has no problem with subtracting nearly equal numbers (cancellation error), which plagues classical difference methods at very small steps hh. Therefore we can choose an extremely small hh (e.g., 10-200) and obtain a practically exact result (down to machine precision).

If we now look at the use of the method on the example from above, exp⁡(−x)\exp(-x), let’s first recall the exact result at the value x=1x=1:

Loading...

When called with a step h=0.1h=0.1 we get two (three) correct digits (similarly to the central difference scheme), and by reducing the step to one tenth we would roughly double the number of correct digits.

Loading...

Since we have no problem with subtracting nearly equal numbers, we can reduce the step extremely and obtain an exact result (down to machine precision):

Loading...
array([0.+0.e+00j, 0.+2.e-05j, 0.+4.e-05j])
array([ 0. +0.00000000e+00j, 0.84147098+1.08060461e-05j, -0.7568025 -2.61457448e-05j])

Let’s also look at an example of differentiating the function sin(x**2). From the code and the figure below we can see a substantially faster convergence of the complex-step method. Why does the method work so well? If we substitute x+i hx+i\,h into x2x^2, we get: (x+i h)2=x2+2i x h−h2(x+i\,h)^2 = x^2 + 2i\,x\,h - h^2

Since hh is very small, h2h^2 is negligible in the real part, while the imaginary part 2 x h2\,x\,h carries linearly through the sine. In this way we obtain an approximation of the theoretical derivative 2 x cos⁡(x2)2\,x\,\cos(x^2). The method sees the derivative in the imaginary part without subtraction, so no round-off error occurs.

<Figure size 1000x600 with 1 Axes>

Additional