Complex Step Differentiation
blogs.mathworks.com
blogs.mathworks.com
There is an implementation of dual numbers in Julia [2] that is quite fun to play around with. The Optim.jl package [3] uses this to get better derivatives than finite differencing.
[1] http://en.wikipedia.org/wiki/Dual_number
Because you are dividing by h, to decrease it from, say 1e-6, to 1e-8 introduces more error into the result than it removes. (Floating-point division with a numerator and denominator that have a big difference in magnitude is a problem.)
The example problem in the article just happened to be well conditioned because the sine and cosine, and their derivatives, are bound on [-1, 1], but most problems won't be and the complex step method won't help much then either.
To get better accuracy, rather than decrease the step size, you should use a higher-order formula. The three and five-point formulas are common. The Handbook of Mathematical Functions (Abramowitz & Stegun) has a table of coefficients for differentiation formulas up to the sixth order (Table 25.2): http://imgur.com/eRl8w8h
For fun, derive the 8-point formulas.
You can use a very small step size (and so get very high accuracy) with traditional finite difference differentiation at x=0 for functions with f(0) = 0. Complex step differentiation takes advantage of the fact that real valued functions of a real argument have 0 imaginary part everywhere on the real line.
> most problems won't be [well conditioned] and the complex step method won't help much then either
Try coming up with an example of this. You might be able to find one if there is a pole or branch cut very near the real line, but otherwise, complex step differentiation is very robust.
This method will also work if, for example, you want to compute W'(z), Gamma'(z), or other special functions.
Try symbolically differentiating deeply nested functions, and you will see that you end up with n^2 terms, where n is the depth of the nesting. E.g. Try differentiating n = 10 for (i=0; i<n; i++) x = sin(x)
Then notice that evaluating this function with a complex argument is only O(n), not O(n^2).