// lnx(). // General includes. #include "cl_sysdep.h" // Specification. #include "cl_F_tran.h" // Implementation. #include "cln/float.h" #include "cl_low.h" #include "cl_F.h" #include "cln/lfloat.h" #include "cl_LF.h" #include "cln/integer.h" #undef MAYBE_INLINE #define MAYBE_INLINE inline #include "cl_LF_zerop.cc" #include "cl_LF_minusp.cc" #include "cl_LF_exponent.cc" namespace cln { // cl_F lnx_naive (const cl_F& x) // cl_LF lnx_naive (const cl_LF& x) // // Methode: // y:=x-1, e := Exponent aus (decode-float y), d := (float-digits y) // Bei y=0.0 oder e<=-d liefere y // (denn bei e<=-d ist y/2 < 2^(-d)/2 = 2^(-d-1), also // 0 <= y - ln(x) < y^2/2 < 2^(-d-1)*y // also ist ln(x)/y, auf d Bits gerundet, gleich y). // Bei e<=-sqrt(d) verwende die Potenzreihe // ln(x) = sum(j=0..inf,(-1)^j*y^(j+1)/(j+1)): // a:=-y, b:=y, i:=1, sum:=0, // while (/= sum (setq sum (+ sum (/ b i)))) do i:=i+1, b:=b*a. // Ergebnis sum. // Sonst setze y := sqrt(x), berechne rekursiv z:=ln(y) // und liefere 2*z = (scale-float z 1). // Aufwand: asymptotisch d^0.5*M(d) = d^2.5 . const cl_LF lnx_naive (const cl_LF& x) { var cl_LF y = x-cl_float(1,x); if (zerop(y)) // y=0.0 -> y als Ergebnis return y; var uintL actuallen = TheLfloat(x)->len; var uintL d = float_digits(x); var sintL e = float_exponent(y); if (e <= -(sintL)d) // e <= -d ? return y; // ja -> y als Ergebnis { Mutable(cl_LF,x); var uintL k = 0; // Rekursionszähler k:=0 // Bei e <= -1-limit_slope*floor(sqrt(d)) kann die Potenzreihe // angewandt werden. // Wähle für ln(1+y), naive1: limit_slope = 1.0, // für ln(1+y), naive2: limit_slope = 11/16 = 0.7, // für atanh(z), naive1: limit_slope = 0.6, // für atanh(z), naive1: limit_slope = 0.5. var sintL e_limit = -1-floor(isqrt(d),2); // -1-floor(sqrt(d)) while (e > e_limit) { // e > -1-floor(sqrt(d)) -> muß |y| verkleinern. x = sqrt(x); // x := (sqrt x) y = x-cl_float(1,x); // y := (- x 1) und e = float_exponent(y); // e neu berechnen k = k+1; // k:=k+1 } if (0) { // Potenzreihe ln(1+y) anwenden: var int i = 1; var cl_LF sum = cl_float(0,x); // sum := (float 0 x) var cl_LF a = -y; var cl_LF b = y; if (0) { // naive1: // floating-point representation loop { var cl_LF new_sum = sum + b/(cl_I)i; // (+ sum (/ b i)) if (new_sum == sum) // = sum ? break; // ja -> Potenzreihe abbrechen sum = new_sum; b = b*a; i = i+1; } } else { // naive2: // floating-point representation with smooth precision reduction var cl_LF eps = scale_float(b,-(sintL)d-10); loop { var cl_LF new_sum = sum + LF_to_LF(b/(cl_I)i,actuallen); // (+ sum (/ b i)) if (new_sum == sum) // = sum ? break; // ja -> Potenzreihe abbrechen sum = new_sum; b = cl_LF_shortenwith(b,eps); b = b*a; i = i+1; } } return scale_float(sum,k); // sum als Ergebnis, wegen Rekursion noch mal 2^k } else { var cl_LF z = y / (x+cl_float(1,x)); // Potenzreihe atanh(z) anwenden: var int i = 1; var cl_LF a = square(z); // a = x^2 var cl_LF b = cl_float(1,x); // b := (float 1 x) var cl_LF sum = cl_float(0,x); // sum := (float 0 x) if (0) { // naive1: // floating-point representation loop { var cl_LF new_sum = sum + b / (cl_I)i; // (+ sum (/ b i)) if (new_sum == sum) // = sum ? break; // ja -> Potenzreihe abbrechen sum = new_sum; b = b*a; i = i+2; } } else { // naive2: // floating-point representation with smooth precision reduction var cl_LF eps = scale_float(b,-(sintL)d-10); loop { var cl_LF new_sum = sum + LF_to_LF(b/(cl_I)i,actuallen); // (+ sum (/ b i)) if (new_sum == sum) // = sum ? break; // ja -> Potenzreihe abbrechen sum = new_sum; b = cl_LF_shortenwith(b,eps); b = b*a; i = i+2; } } return scale_float(sum*z,k+1); // 2*sum*z als Ergebnis, wegen Rekursion noch mal 2^k } }} // Bit complexity (N = length(x)): O(N^(1/2)*M(N)). const cl_F lnx_naive (const cl_F& x) { if (longfloatp(x)) { DeclareType(cl_LF,x); return lnx_naive(x); } var cl_F y = x-cl_float(1,x); if (zerop(y)) // y=0.0 -> y als Ergebnis return y; var uintL d = float_digits(x); var sintL e = float_exponent(y); if (e <= -(sintL)d) // e <= -d ? return y; // ja -> y als Ergebnis { Mutable(cl_F,x); var uintL k = 0; // Rekursionszähler k:=0 // Bei e <= -1-floor(sqrt(d)) kann die Potenzreihe angewandt werden. var sintL e_limit = -1-isqrt(d); // -1-floor(sqrt(d)) while (e > e_limit) { // e > -1-floor(sqrt(d)) -> muß |y| verkleinern. x = sqrt(x); // x := (sqrt x) y = x-cl_float(1,x); // y := (- x 1) und e = float_exponent(y); // e neu berechnen k = k+1; // k:=k+1 } // Potenzreihe anwenden: var int i = 1; var cl_F sum = cl_float(0,x); // sum := (float 0 x) var cl_F a = -y; var cl_F b = y; loop { var cl_F new_sum = sum + b/(cl_I)i; // (+ sum (/ b i)) if (new_sum == sum) // = sum ? break; // ja -> Potenzreihe abbrechen sum = new_sum; b = b*a; i = i+1; } return scale_float(sum,k); // sum als Ergebnis, wegen Rekursion noch mal 2^k }} // Bit complexity (N = length(x)): O(N^(1/2)*M(N)). const cl_LF lnx_ratseries (const cl_LF& x) { // Method: // Based on the same ideas as expx_ratseries. // y := 0. // Loop // [x*exp(y) is invariant] // x' := x-1. If x' = 0, terminate the loop. // Choose approximation y' of log(x) = log(1+x'): // If |x'| >= 1/2, set y' = 1/2 * sign(x'). // If |x'| < 2^-n with n maximal, set // y' = truncate(x'*2^(2n))/2^(2n). // Set y := y + y' and x := x*exp(-y'). var uintC len = TheLfloat(x)->len; { Mutable(cl_LF,x); var cl_LF y = cl_I_to_LF(0,len); loop { var cl_LF x1 = x + cl_I_to_LF(-1,len); var cl_idecoded_float x1_ = integer_decode_float(x1); // x1 = (-1)^sign * 2^exponent * mantissa if (zerop(x1_.mantissa)) break; var uintL lm = integer_length(x1_.mantissa); var uintL me = cl_I_to_UL(- x1_.exponent); var cl_I p; var uintL lq; var cl_boolean last_step = cl_false; if (lm >= me) { // |x'| >= 1/2 ? p = x1_.sign; // 1 or -1 lq = 1; } else { var uintL n = me - lm; // |x'| < 2^-n with n maximal // Set p to the first n bits of |x'|: if (lm > n) { p = x1_.mantissa >> (lm - n); lq = 2*n; } else { p = x1_.mantissa; lq = lm + n; } if (minusp(x1_.sign)) { p = -p; } // If 2*n >= lm = intDsize*len, then within our // precision exp(-y') = 1-y', (because |y'^2| < 2^-lm), // and we know a priori that the iteration will stop // after the next big multiplication. This saves one // big multiplication at the end. if (2*n >= lm) last_step = cl_true; } y = y + scale_float(cl_I_to_LF(p,len),-(sintL)lq); if (last_step) break; x = x * cl_exp_aux(-p,lq,len); } return y; }} // Bit complexity (N = length(x)): O(log(N)^2*M(N)). // Timings of the above algorithms, on an i486 33 MHz, running Linux, // applied to x = sqrt(sqrt(2)) = 1.189... // N ln(1+y) ln(1+y) atanh z atanh z exp // naive1 naive2 naive1 naive2 ratseries // 10 0.019 0.016 0.013 0.012 0.036 // 25 0.077 0.056 0.057 0.040 0.087 // 50 0.30 0.21 0.23 0.15 0.21 // 100 1.24 0.81 0.92 0.59 0.61 // 250 8.8 5.8 6.3 4.3 2.77 // 500 43.9 28.8 29.7 21.0 9.8 // 1000 223 149 144 107 30 // ==> ratseries faster for N >= 110. (N = length before extended by the caller.) } // namespace cln