Your implementation is a good approximation (since derivative is a limit and h
is a finite value). However, I suggest some different code:
public static class MyMath {
// static: we don't want "this"
// Func<double, double> return value: derivative is a function, not a value.
// If we want a point - double - let's name the method as DerivativeAt
// No h - we can't provide correct h for all possible x
public static Func<double, double> Derivative(Func<double, double> function) {
//DONE: Validate public methods arguments
if (null == function)
throw new ArgumentNullException("function");
return new Func<double, double>((x) => {
// Let's compute h for given x
// Easiest, but not the best
double h = Math.Abs(x) < 1e-10 ? 1e-16 : x / 1.0e6;
// "Central" derivative is often a better choice then right one ((f(x + h) - f(x))/h)
return (function(x + h) - function(x - h)) / (2.0 * h);
});
}
// h = 0.0: be nice and let user has no idea what step is reasonable
public static double DerivativeAt(Func<double, double> function,
double x,
double h = 0.0) {
//DONE: Validate public methods arguments
if (null == function)
throw new ArgumentNullException("function");
// If user don't want to provide h, let's compute it
if (0 == h)
h = Math.Abs(x) < 1e-10 ? 1e-16 : x / 1.0e6; // Easiest, but not the best
// "Central" derivative is often a better choice then right one ((f(x + h) - f(x))/h)
return (function(x + h) - function(x - h)) / (2.0 * h);
}
}
If you frequently use Derivative
you can try declaring it as an extension method:
public static Func<double, double> Derivative(this Func<double, double> function) {...}
public static double DerivativeAt(this Func<double, double> function,
double x,
double h = 0.0) { ... }
Demo: let's find out maximum error when x
within [0 .. 2 * PI)
range for Sin
function
// We don't want to repeat pesky "MyMath" in "MyMath.Derivative"
using static MyNamespace.MyMath;
...
// Derivative of Sin (expected to be Cos)
var d_sin = Derivative(x => Math.Sin(x));
double maxError = Enumerable
.Range(0, 1000)
.Select(i => 2.0 * Math.PI * i / 1000.0)
.Select(x => Math.Abs(d_sin(x) - Math.Cos(x))) // d(sin(x)) / dx == cos(x)
.Max();
Console.WriteLine(maxError);
Outcome:
1.64271596325705E-10
Edit: "Central" derivative.
As we know, derivative is a limit
df/dx == lim (f(x + h) - f(x)) / h
h -> 0
however we can ask: how h
tend to 0
. We have a lot of ways in case of complex numbers (h
can, say, spiral down to 0
or goes along a strait line); in case of real numbers h
can be either positive (right semi-derivative) or negative (left semi-derivative). Usually (standard definition) we require left semi-deivative be equal to right one in order to have derivative:
d+f(x) == d-f(x) == df/dx
However, sometime we use lenient definition ("central" derivative):
df/dx == (d+f(x) + d-f(x)) / 2
For instance, d(abs(x))/dx
at x = 0
d-abs(x) = -1
d+abs(x) = 1
d abs(x) / dx doesn't exist (standard definition)
d abs(x) / dx = 0 "central", lenient definition.
Please, note that you current code computes in fact right semi-derivative; in case of Abs(x)
you'll get wrong 1
. 0
is a better answer in the context if not in calculus but in, say, engineering (imagine a moving car which does have a velocity). Another issue is that when computing derivative at x
we don't need f
exist at x
. For instance
f(x) = x / abs(x) which can be put as
-1 when x < 0
f(x) = doesn't exist when x = 0
+1 when x > 0
Please, note that derivative df/dx
at x = 0
exists (it's positive infinity). That's why when computing derivative we should avoid computing f(x)
. You current code will return
(h / h + 0 / 0) / h == (1 + NaN) / h == NaN
double.NaN
- derivative doesn't exists (and that is wrong); "central" derivative will return
(h/h - -h/h) / (2 * h) == 1 / h == some huge number (approximation of +Inf)