// expx(). // 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_exponent.cc" namespace cln { // cl_F expx_naive (const cl_F& x) // cl_LF expx_naive (const cl_LF& x) // // Methode: // e := Exponent aus (decode-float x), d := (float-digits x) // Bei x=0.0 oder e<-d liefere 1.0 // (denn bei e<=-d-1 ist abs(exp(x)-1) = abs(x)+O(x^2) < 2^(-d-1), // also ist exp(x), auf d Bits gerundet, gleich 1.0). // Bei e<=-sqrt(d) verwende die Potenzreihe // exp(x) = sum(j=0..inf,x^j/j!): // b:=1, i:=0, sum:=0, // while (/= sum (setq sum (+ sum b))) do b:=b*x/(i+1), i:=i+1. // Ergebnis sum. // Sonst setze y := x/2 = (scale-float x -1), // berechne rekursiv z:=exp(y) und liefere z^2. // Aufwand: asymptotisch d^2.5 . const cl_LF expx_naive (const cl_LF& x) { // Methode: // wie oben, mit adaptiver Genauigkeit während der Potenzreihen-Summation. if (zerop(x)) return cl_float(1,x); var uintL actuallen = TheLfloat(x)->len; var uintL d = float_digits(x); var sintL e = float_exponent(x); if (e < -(sintL)d) // e < -d ? return cl_float(1,x); // ja -> 1.0 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. limit_slope = 1.0 ist nicht schlecht, // auch im Bereich d = ca. 800. var sintL e_limit = -1-isqrt(d); // -1-floor(sqrt(d)) if (e > e_limit) { // e > -1-floor(sqrt(d)) -> muß |x| verkleinern. k = e - e_limit; x = scale_float(x,-(sintL)k); // x := x/2^k // Neuer Exponent = e-k = e_limit. } // Potenzreihe anwenden: var int i = 0; var cl_LF b = cl_float(1,x); // b := (float 1 x) var cl_LF eps = scale_float(b,-(sintL)d-10); var cl_LF sum = cl_float(0,x); // sum := (float 0 x) loop { var cl_LF new_sum = sum + LF_to_LF(b,actuallen); if (new_sum == sum) // = sum ? break; // ja -> Potenzreihe abbrechen sum = new_sum; i = i+1; b = cl_LF_shortenwith(b,eps); b = (b*x)/(cl_I)i; // b := b*x/i } var cl_LF& result = sum; // sum als Ergebnis // Wegen Rekursion noch k mal quadrieren: for ( ; k > 0; k--) result = square(result); return result; }} // Bit complexity (N = length(x)): O(N^(1/2)*M(N)). const cl_F expx_naive (const cl_F& x) { if (longfloatp(x)) { DeclareType(cl_LF,x); return expx_naive(x); } if (zerop(x)) return cl_float(1,x); var uintL d = float_digits(x); var sintL e = float_exponent(x); if (e < -(sintL)d) // e < -d ? return cl_float(1,x); // ja -> 1.0 als Ergebnis { Mutable(cl_F,x); var uintL k = 0; // Rekursionszähler k:=0 // Bei e <= -1-limit_slope*floor(sqrt(d)) kann die Potenzreihe // angewandt werden. limit_slope = 1.0 ist nicht schlecht. Für // d > 1600 scheint der Bereich 2.0 <= limit_slope <= 2.6 am besten // zu sein (mit bis zu 15% Beschleunigung gegenüber limit_slope = 1.0), // aber in diesem Bereich rechnen wir gar nicht. // Wir wählen limit_slope = 1.5. var sintL e_limit = -1-floor(isqrt(d)*3,2); // -1-floor(sqrt(d)) if (e > e_limit) { // e > -1-floor(sqrt(d)) -> muß |x| verkleinern. k = e - e_limit; x = scale_float(x,-(sintL)k); // x := x/2^k // Neuer Exponent = e-k = e_limit. } // Potenzreihe anwenden: var int i = 0; var cl_F b = cl_float(1,x); // b := (float 1 x) var cl_F sum = cl_float(0,x); // sum := (float 0 x) loop { var cl_F new_sum = sum + b; if (new_sum == sum) // = sum ? break; // ja -> Potenzreihe abbrechen sum = new_sum; i = i+1; b = (b*x)/(cl_I)i; // b := b*x/i } var cl_F& result = sum; // sum als Ergebnis // Wegen Rekursion noch k mal quadrieren: for ( ; k > 0; k--) result = square(result); return result; }} // Bit complexity (N = length(x)): O(N^(1/2)*M(N)). const cl_LF expx_ratseries (const cl_LF& x) { // [Jonathan M. Borwein, Peter B. Borwein: Pi and the AGM. // Wiley 1987. Section 10.2.3] var uintC len = TheLfloat(x)->len; var cl_idecoded_float x_ = integer_decode_float(x); // x = (-1)^sign * 2^exponent * mantissa var uintL lq = cl_I_to_UL(- x_.exponent); var const cl_I& p = x_.mantissa; // Compute exp(p/2^lq) by splitting into pieces. // Each piece gives rise to a factor exp(pk/2^lqk). // Instead of the standard choice lqk = 2^k, we choose // lqk = c^k + O(1), where c > 1 is real. // Running time on Linux i486, 33 Mhz, computing exp(sqrt(2)-1): // c 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0 3.1 3.2 3.3 3.4 3.5 // (a) 400 393 390 377 371 360 363 367 367 358 362 362 363 362 376 372 // (b) 311 317 305 312 295 291 286 293 291 284 295 284 293 287 288 305 // (a): N=300, time in 0.01 sec. (b): N=1000, time in 0.1 sec. // Values 2.5 <= c <= 3.2 seem best. Let's choose c = 2.875. var cl_boolean first_factor = cl_true; var cl_LF product; var uintL b1; var uintL b2; for (b1 = 0, b2 = 1; b1 < lq; b1 = b2, b2 = ceiling(b2*23,8)) { // Piece containing bits b1+1..b2 after "decimal point" // in the binary representation of (p/2^lq). var uintL lqk = (lq >= b2 ? b2 : lq); var cl_I pk = ldb(p,cl_byte(lqk-b1,lq-lqk)); // Compute exp(pk/2^lqk). if (!zerop(pk)) { if (minusp(x_.sign)) { pk = -pk; } var cl_LF factor = cl_exp_aux(pk,lqk,len); if (first_factor) { product = factor; first_factor = cl_false; } else product = product * factor; } } if (first_factor) return cl_I_to_LF(1,len); else return product; } // 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(2)-1 = 0.414... // ("naive" with adaptive limit_slope, about sqrt(ln(len)).) // N naive ratseries // 10 0.010 0.027 // 25 0.039 0.072 // 50 0.15 0.19 // 100 0.60 0.55 // 250 3.9 2.6 // 500 16.3 9.3 // 1000 68 29 // ==> ratseries faster for N >= 84. } // namespace cln