如果这是您第一次编写 Mandelbrot 生成器,我建议您使用该Complex
类型。过早的优化是万恶之源;而且,Mandelbrot 的一半酷之处在于您可以在几行代码中生成令人惊叹的图像。花两个小时来实现你自己的BigDecimal
那种失败的目的。
但是,我认为您没有提到另一种选择:使用定点数学代替 IEEE 浮点数。定点运算对于 Mandelbrot 生成确实很方便,因为加法完全一样,乘法几乎一样:(x * y)
变成(x * y) >> MANTISSABITS
.
剧透警告!这是我几年前写的一个定点 Mandelbrot 生成器。它使用小数点后的 28 位。如果您的工具链支持int128_t
,或者您不介意进入汇编程序,或者您不介意长时间进行乘法运算,则可以轻松替换int32_t
为int64_t
并获得 60 位分辨率。
#include <stdio.h>
#include <stdint.h>
#include "ImageFmtc.h"
/* Represent each component of the complex number
* as a fixed-point value between -4 and +4. There
* are 4 bits before the binary point and 28 bits after. */
typedef int32_t REALFIXED;
typedef int32_t IMAGFIXED;
#define MandelWidth 300
#define MandelHeight 300
unsigned char Image[MandelWidth][MandelHeight];
void mandelbrot(REALFIXED centerx, IMAGFIXED centery, REALFIXED windowsize)
{
int x, y;
unsigned char *image_ptr = &Image[0][0];
for (y = 0; y < MandelHeight; ++y) {
const IMAGFIXED z_imag = (1LL * windowsize * y / MandelHeight) + centery;
for (x = 0; x < MandelWidth; ++x) {
const REALFIXED z_real = (1LL * windowsize * x / MandelWidth) + centerx;
REALFIXED t_real = z_real;
IMAGFIXED t_imag = z_imag;
int iter;
for (iter=0; iter < 255; ++iter) {
long long r2 = (1uLL * t_real * t_real) >> 28;
long long i2 = (1uLL * t_imag * t_imag) >> 28;
long long two_ri = (1uLL * t_real * t_imag) >> 27;
if (r2 + i2 > (4LL << 28))
break;
t_real = (r2 - i2) + z_real;
t_imag = (two_ri) + z_imag;
}
*image_ptr++ = iter & 0xFF;
}
}
return;
}
REALFIXED double2fixed(double x)
{
return (REALFIXED)(x * (1uLL << 28));
}
int main(int argc, char **argv)
{
double cx, cy, scale;
sscanf(argv[1], "%lf", &cx);
sscanf(argv[2], "%lf", &cy);
sscanf(argv[3], "%lf", &scale);
mandelbrot(double2fixed(cx-scale/2), double2fixed(cy-scale/2), double2fixed(scale));
WritePGM5("mandel.pgm", (unsigned char *)Image, MandelWidth, MandelHeight);
return 0;
}
关于太阳系的大小:即使您的数字可能具有 60 位的分辨率,但这并不意味着您实际上可以计算出以 60 位分辨率设置的 Mandelbrot 像素精确渲染。乘法上的舍入误差意味着当你放大时,你的图像会变得比你预期的要快得多。我确定有人做过数学计算,但我不知道它是不是在我的脑海中。关键字“错误传播”。