/* Logarithm. Computes log(x) in double-double precision. This is a natural logarithm (i.e., base e). */ double2 dd_log(const double2 a) { /* Strategy. The Taylor series for log converges much more slowly than that of exp, due to the lack of the factorial term in the denominator. Hence this routine instead tries to determine the root of the function f(x) = exp(x) - a using Newton iteration. The iteration is given by x' = x - f(x)/f'(x) = x - (1 - a * exp(-x)) = x + a * exp(-x) - 1. Only one iteration is needed, since Newton's iteration approximately doubles the number of digits per iteration. */ double2 x; if (dd_is_one(a)) { return DD_C_ZERO; } if (a.x[0] <= 0.0) { dd_error("(dd_log): Non-positive argument."); return DD_C_NAN; } x = dd_create_d(log(a.x[0])); /* Initial approximation */ /* x = x + a * exp(-x) - 1.0; */ x = dd_add(x, dd_sub(dd_mul(a, dd_exp(dd_neg(x))), DD_C_ONE)); return x; }
double2 dd_sqrt(const double2 a) { /* Strategy: Use Karp's trick: if x is an approximation to sqrt(a), then sqrt(a) = a*x + [a - (a*x)^2] * x / 2 (approx) The approximation is accurate to twice the accuracy of x. Also, the multiplication (a*x) and [-]*x can be done with only half the precision. */ double x, ax; if (dd_is_zero(a)) return DD_C_ZERO; if (dd_is_negative(a)) { dd_error("(dd_sqrt): Negative argument."); return DD_C_NAN; } x = 1.0 / sqrt(a.x[0]); ax = a.x[0] * x; return dd_add_d_d(ax, dd_sub(a, dd_sqr_d(ax)).x[0] * (x * 0.5)); }
double2 polyroot(const double2 *c, int n, const double2 x0, int max_iter, double thresh) { double2 x = x0; double2 f; double2 *d = DD_STATIC_CAST(double2 *, calloc(sizeof(double2), n)); int conv = 0; int i; double max_c = fabs(dd_to_double(c[0])); double v; if (thresh == 0.0) { thresh = DD_C_EPS; } /* Compute the coefficients of the derivatives. */ for (i = 1; i <= n; i++) { v = fabs(dd_to_double(c[i])); if (v > max_c) { max_c = v; } d[i - 1] = dd_mul_dd_d(c[i], DD_STATIC_CAST(double, i)); } thresh *= max_c; /* Newton iteration. */ for (i = 0; i < max_iter; i++) { f = polyeval(c, n, x); if (fabs(dd_to_double(f)) < thresh) { conv = 1; break; } x = dd_sub(x, (dd_div(f, polyeval(d, n - 1, x)))); } free(d); if (!conv) { dd_error("(dd_polyroot): Failed to converge."); return DD_C_NAN; } return x; }
double2 dd_exp(const double2 a) { /* Strategy: We first reduce the size of x by noting that exp(kr + m * log(2)) = 2^m * exp(r)^k where m and k are integers. By choosing m appropriately we can make |kr| <= log(2) / 2 = 0.347. Then exp(r) is evaluated using the familiar Taylor series. Reducing the argument substantially speeds up the convergence. */ const double k = 512.0; const double inv_k = 1.0 / k; double m; double2 r, s, t, p; int i = 0; if (a.x[0] <= -709.0) { return DD_C_ZERO; } if (a.x[0] >= 709.0) { return DD_C_INF; } if (dd_is_zero(a)) { return DD_C_INF; } if (dd_is_one(a)) { return DD_C_E; } m = floor(a.x[0] / DD_C_LOG2.x[0] + 0.5); r = dd_mul_pwr2(dd_sub(a, dd_mul_dd_d(DD_C_LOG2, m)), inv_k); p = dd_sqr(r); s = dd_add(r, dd_mul_pwr2(p, 0.5)); p = dd_mul(p, r); t = dd_mul(p, inv_fact[0]); do { s = dd_add(s, t); p = dd_mul(p, r); ++i; t = dd_mul(p, inv_fact[i]); } while (fabs(dd_to_double(t)) > inv_k * DD_C_EPS && i < 5); s = dd_add(s, t); s = dd_add(dd_mul_pwr2(s, 2.0), dd_sqr(s)); s = dd_add(dd_mul_pwr2(s, 2.0), dd_sqr(s)); s = dd_add(dd_mul_pwr2(s, 2.0), dd_sqr(s)); s = dd_add(dd_mul_pwr2(s, 2.0), dd_sqr(s)); s = dd_add(dd_mul_pwr2(s, 2.0), dd_sqr(s)); s = dd_add(dd_mul_pwr2(s, 2.0), dd_sqr(s)); s = dd_add(dd_mul_pwr2(s, 2.0), dd_sqr(s)); s = dd_add(dd_mul_pwr2(s, 2.0), dd_sqr(s)); s = dd_add(dd_mul_pwr2(s, 2.0), dd_sqr(s)); s = dd_add(s, DD_C_ONE); return dd_ldexp(s, DD_STATIC_CAST(int, m)); }
inline void calculate_pixel(double x, double y, unsigned long *lastit, double *zxd, double *zyd, bool *inside) { DoubleDouble px, py, zx, zy, xx, yy; //px = x*x0d + x2; px = dd_mul_d(x0d, x); px = dd_add(px, x2); //py = y*y1d + y2; py = dd_mul_d(y1d, y); py = dd_add(py, y2); // no Main bulb or Cardoid check to be faster zx = dd_new(px.hi, px.lo); zy = dd_new(py.hi, py.lo); unsigned long i; *inside = true; int check = 3; int whenupdate = 10; double hx, hy, d; hx = 0; hy = 0; //for (i = 1; i <= maxiter; i++) { for (i = 1; i <= 50000; i++) { //xx = zx * zx; xx = dd_sqr(zx); //yy = zy * zy; yy = dd_sqr(zy); //if (xx + yy > bailout) { if (xx.hi + yy.hi > bailout) { *inside = false; break; } // iterate //zy = 2 * zx * zy + py; //zx = dd_mul_ui(zx, 2); //zy = dd_mul(zx, zy); zy = dd_add(dd_mul2(zx, zy), py); //zx = xx - yy + px; zx = dd_add(dd_sub(xx, yy), px); // period checking d = zx.hi - hx; if (d > 0.0 ? d < eps : d > -eps) { d = zy.hi - hy; if (d > 0.0 ? d < eps : d > -eps) { // Period found. break; } } if ((i & check) == 0) { if (--whenupdate == 0) { whenupdate = 10; check <<= 1; check++; } // period = 0; hx = zx.hi; hy = zy.hi; } } *lastit = i; *zxd = zx.hi; *zyd = zy.hi; }