Hi everyone,
I am trying to write a little program to draw fractals in C, and I am using Ultra Fractal as an example.
I implemented triangle inequality average coloring (TIA), but my results differ a bit from UltraFractal. I understand that to obtain a good image with TIA, one has to chose a big bailout radius, (like 1E20), but with my program I get artifacts for such big values.
The weird thing is, my program uses double precision floats, like Ultra Fractal by default (I checked in the "statistics" panel that the type used is indeed double), so I just don't understand why Ultra Fractal doesn't show any artifacts.
To illustrate what I am talking about, below is what I obtain using TIA with my program, for a bailout value of 1E20 (this is the classic mandelbrot set).
First with no interpolation :
http://dl.free.fr/k4lvSLyZGWith linear interpolation (like Ultra Fractal) :
http://dl.free.fr/nukvlbBleAnd here is the result with Ultra Fractal (gradients differ, but apart from that it should be the same) :
http://dl.free.fr/ntgp4MLS5Note that the artifacts do also appear with Ultra Fractal for a bigger bailout radius, like 1E50 (unless you increase the precision, so that Ultra Fractal switches to Arbitrary precision floats) :
http://dl.free.fr/h3eWZMiAIAlso, if I reduce the bailout value to something 1E7 in my program, the artifacts disappear.
First with no interpolation (see ? no artifacts) :
http://dl.free.fr/bXmTqM21VThen with linear interpolation (like UltraFractal) :
http://dl.free.fr/rjSZElo1FNow I can explain why MY program shows such artifacts, but I can't explain why Ultra Fractal does NOT. Here is the explanation :
At one point during the TIA computation, you have to compute something like ||z-c|-|c|| (where c is denoted as #pixel in Ultra Fractal scripts, for those who now). Now if |z| is much much bigger than |c|, then |z-c| ~= |z| : in particular, if your float is not precise enough, you will have |z-c|=|c| (which leads to those artifacts). You also experience the same thing when you compute z_{n+1} = z_n^2 + c : you may have z_n^2+c = z_n^2 because of lack of precision.
For example : In C, mantissa for double precision numbers is 52 bits, so about 14-15 decimal digits. So if |z|>1E16 (which happens when bailout radius is > 1E20), and 0<|c|<10 (which is the case for the given example), then double precision numbers are not precise enough to evaluate |z-c| (we will actually have |z-c|=|z|).
Try initializing a double with 2E20+3E-1, and print it, you will get 2E20 (because 2000000000000000000003 does not fit in 52 bits).
Anyone has any idea how Ultra Fractal can compute things like that with only double ? Because that just seems impossible..
Edit : found this in the Ultra Fractal 8 manual on triangle inequality average :
"Use very large bail-out values for good results. The default value 1e20 (a 1 with 20 zeroes) is a good
starting point. The Mandelbrot (Built-in) formula cannot handle such large values, so use the non-
built-in Mandelbrot instead."
I checked, and indeed, with the Mandelbrot (Built-in) formula, the artifacts appear at a bailout radius of 1e12 or something (and with less, the result is not even smooth, as though there was no linear interpolation), like for my program which uses doubles. So apparently, the problem does not come from my implementation, and the trick for Ultra Fractal is probably that for non builtin formulas it does not use double precision numbers (even if it says so), but something else, maybe double double or quad precision numbers
. What do you think ?