]> www.ginac.de Git - cln.git/blob - src/float/transcendental/cl_F_atanx.cc
2006-04-25 Bruno Haible <bruno@clisp.org>
[cln.git] / src / float / transcendental / cl_F_atanx.cc
1 // atanx().
2
3 // General includes.
4 #include "cl_sysdep.h"
5
6 // Specification.
7 #include "cl_F_tran.h"
8
9
10 // Implementation.
11
12 #include "cln/float.h"
13 #include "cl_low.h"
14 #include "cl_F.h"
15 #include "cln/lfloat.h"
16 #include "cl_LF.h"
17 #include "cln/integer.h"
18
19 #undef MAYBE_INLINE
20 #define MAYBE_INLINE inline
21 #include "cl_LF_zerop.cc"
22 #include "cl_LF_minusp.cc"
23 #include "cl_LF_exponent.cc"
24
25 namespace cln {
26
27 // cl_F atanx_naive (const cl_F& x)
28 // cl_LF atanx_naive (const cl_LF& x)
29 //
30 // Methode:
31 // e := Exponent aus (decode-float x), d := (float-digits x)
32 // Bei x=0.0 oder e<=-d/2 liefere x
33 //   (denn bei e<=-d/2 ist x^2/3 < x^2/2 < 2^(-d)/2 = 2^(-d-1), also
34 //   1 >= atan(x)/x > 1-x^2/3 > 1-2^(-d-1),
35 //   also ist atan(x)/x, auf d Bits gerundet, gleich 1.0).
36 // Bei e<=-sqrt(d) verwende die Potenzreihe
37 //   atan(x)/x = sum(j=0..inf,(-x^2)^j/(2j+1)):
38 //   a:=-x^2, b:=1, i:=1, sum:=0,
39 //   while (/= sum (setq sum (+ sum (/ b i)))) do i:=i+2, b:=b*a.
40 //   Ergebnis x*sum.
41 // Sonst setze y := x/(1+sqrt(1+x^2)), berechne rekursiv z:=atan(y)
42 //   und liefere 2*z = (scale-float z 1).
43 // Diese Rekursion wird entrekursiviert. Statt k mal hintereinander
44 //   x := x/(1+sqrt(1+x^2)) zu bilden, arbeitet man lieber mit den Kehrwerten,
45 //   setzt also x := 1/|x|, dann k mal x := x+sqrt(x^2+1), dann x := +- 1/x.
46 // Aufwand: asymptotisch d^2.5 .
47
48 static const cl_LF atanx_naive (const cl_LF& x)
49 {
50         if (zerop(x))
51                 return x;
52         var uintC actuallen = TheLfloat(x)->len;
53         var uintC d = float_digits(x);
54         var sintL e = float_exponent(x);
55         if (e <= (sintC)(-d)>>1) // e <= -d/2 <==> e <= -ceiling(d/2)
56                 return x; // ja -> x als Ergebnis
57         var uintL k = 0; // Rekursionszähler k:=0
58         // Bei e <= -1-limit_slope*floor(sqrt(d)) kann die Potenzreihe
59         // angewandt werden. limit_slope = 1.0 ist schlecht (ca. 20% zu
60         // schlecht). Ein guter Wert ist:
61         // Für naive1: limit_scope = 0.5.
62         // Für naive2: limit_scope = 0.375 (ca. 0.5 für kleine len, 0.35 für
63         // große len).
64         var uintL sqrt_d = floor(isqrt(d)*3,8); // limit_slope*floor(sqrt(d))
65         var cl_LF xx = x;
66         if (e >= (sintL)(-sqrt_d)) {
67                 // e > -1-limit_slope*floor(sqrt(d)) -> muß |x| verkleinern.
68                 var sintL e_limit = 1+sqrt_d; // 1+limit_slope*floor(sqrt(d))
69                 xx = recip(abs(xx)); // 1/|x|
70                 do {
71                   // nächstes x nach der Formel x := x+sqrt(x^2 + 1) berechnen:
72                   xx = sqrt(square(xx) + cl_float(1,xx)) + xx;
73                   k = k+1;
74                 } until (float_exponent(xx) > e_limit);
75                 // Schleifenende mit Exponent(x) > 1+limit_slope*floor(sqrt(d)),
76                 // also x >= 2^(1+limit_slope*floor(sqrt(d))),
77                 // also 1/x <= 2^(-1-limit_slope*floor(sqrt(d))).
78                 // Nun kann die Potenzreihe auf 1/x angewandt werden.
79                 xx = recip(xx);
80                 if (minusp(x))
81                         xx = - xx; // Vorzeichen wieder rein
82         }
83         // Potenzreihe anwenden:
84         var int i = 1;
85         var cl_LF a = - square(xx); // a = - x^2
86         var cl_LF b = cl_float(1,xx); // b := (float 1 x)
87         var cl_LF sum = cl_float(0,xx); // sum := (float 0 x)
88         if (0) {
89                 // naive1:
90                 // floating-point representation
91                 loop {
92                         var cl_LF new_sum = sum + b / (cl_I)i; // (+ sum (/ b i))
93                         if (new_sum == sum) // = sum ?
94                                 break; // ja -> Potenzreihe abbrechen
95                         sum = new_sum;
96                         b = b*a;
97                         i = i+2;
98                 }
99         } else {
100                 // naive2:
101                 // floating-point representation with smooth precision reduction
102                 var cl_LF eps = scale_float(b,-(sintC)d-10);
103                 loop {
104                         var cl_LF new_sum = sum + LF_to_LF(b/(cl_I)i,actuallen); // (+ sum (/ b i))
105                         if (new_sum == sum) // = sum ?
106                                 break; // ja -> Potenzreihe abbrechen
107                         sum = new_sum;
108                         b = cl_LF_shortenwith(b,eps);
109                         b = b*a;
110                         i = i+2;
111                 }
112         }
113         var cl_LF erg = sum*xx; // sum*x als Ergebnis
114         return scale_float(erg,k); // wegen Rekursion noch mal 2^k
115 }
116 // Bit complexity (N = length(x)): O(N^(1/2)*M(N)).
117
118 static const cl_F atanx_naive (const cl_F& x)
119 {
120         if (zerop(x))
121                 return x;
122         var uintC d = float_digits(x);
123         var sintL e = float_exponent(x);
124         if (e <= (sintC)(-d)>>1) // e <= -d/2 <==> e <= -ceiling(d/2)
125                 return x; // ja -> x als Ergebnis
126         var uintL k = 0; // Rekursionszähler k:=0
127         var uintL sqrt_d = floor(isqrt(d),2); // limit_slope*floor(sqrt(d))
128         // Bei e <= -1-limit_slope*floor(sqrt(d)) kann die Potenzreihe
129         // angewandt werden. limit_slope = 1.0 ist schlecht (ca. 20% zu
130         // schlecht). Ein guter Wert ist limit_scope = 0.5.
131         var cl_F xx = x;
132         if (e >= (sintL)(-sqrt_d)) {
133                 // e > -1-limit_slope*floor(sqrt(d)) -> muß |x| verkleinern.
134                 var sintL e_limit = 1+sqrt_d; // 1+limit_slope*floor(sqrt(d))
135                 xx = recip(abs(xx)); // 1/|x|
136                 do {
137                   // nächstes x nach der Formel x := x+sqrt(x^2 + 1) berechnen:
138                   xx = sqrt(square(xx) + cl_float(1,xx)) + xx;
139                   k = k+1;
140                 } until (float_exponent(xx) > e_limit);
141                 // Schleifenende mit Exponent(x) > 1+limit_slope*floor(sqrt(d)),
142                 // also x >= 2^(1+limit_slope*floor(sqrt(d))),
143                 // also 1/x <= 2^(-1-limit_slope*floor(sqrt(d))).
144                 // Nun kann die Potenzreihe auf 1/x angewandt werden.
145                 xx = recip(xx);
146                 if (minusp(x))
147                         xx = - xx; // Vorzeichen wieder rein
148         }
149         // Potenzreihe anwenden:
150         var int i = 1;
151         var cl_F a = - square(xx); // a = - x^2
152         var cl_F b = cl_float(1,xx); // b := (float 1 x)
153         var cl_F sum = cl_float(0,xx); // sum := (float 0 x)
154         loop {
155                 var cl_F new_sum = sum + b / (cl_I)i; // (+ sum (/ b i))
156                 if (new_sum == sum) // = sum ?
157                         break; // ja -> Potenzreihe abbrechen
158                 sum = new_sum;
159                 b = b*a;
160                 i = i+2;
161         }
162         var cl_F erg = sum*xx; // sum*x als Ergebnis
163         return scale_float(erg,k); // wegen Rekursion noch mal 2^k
164 }
165 // Bit complexity (N = length(x)): O(N^(1/2)*M(N)).
166
167 static const cl_LF atanx_ratseries (const cl_LF& t)
168 {
169         // Method:
170         // Based on the same ideas as lnx_ratseries.
171         //   e := exponent of (decode-float t), d := (float-digits t).
172         //   If t=0.0 or e<=-d/2, return t.
173         // (x,y) := (1/sqrt(1+t^2),t/sqrt(1+t^2)), z := 0.
174         // Loop
175         //   [(x+i*y)*exp(i*z) is invariant, x>0, sqrt(x^2+y^2)=1]
176         //   e := exponent of (decode-float y), d := (float-digits y).
177         //   If y=0.0 or e<=-d/2, return z+y
178         //   (because if e<=-d/2 then |y|^3/6 < 2^(-d)/2*|y|, and since
179         //   asin(y) = y+y^3/6+..., asin(y) rounded to d bits is = y).
180         //   Choose approximation z' of angle(x+i*y):
181         //     If |y| >= 1/2, set z' = 1/2 * sign(y).
182         //     If |y| < 2^-n with n maximal, set
183         //       z' = truncate(y*2^(2n))/2^(2n).
184         //   Set z := z + z' and x+i*y := (x+i*y)*exp(-i*z').
185         var uintC len = TheLfloat(t)->len;
186         var uintC d = intDsize*len;
187         if (zerop(t) || (float_exponent(t) <= (sintC)(-d)>>1))
188                 return t;
189         var cl_LF x = recip(sqrt(cl_I_to_LF(1,len) + square(t)));
190         var cl_LF y = t*x;
191         var cl_LF z = cl_I_to_LF(0,len);
192         loop {
193                 if (zerop(y) || (float_exponent(y) <= (sintC)(-d)>>1))
194                         break;
195                 var cl_idecoded_float y_ = integer_decode_float(y);
196                 // y = (-1)^sign * 2^exponent * mantissa
197                 var uintC lm = integer_length(y_.mantissa);
198                 var uintL me = cl_I_to_UL(- y_.exponent);
199                 var cl_I p;
200                 var uintC lq;
201                 var cl_boolean last_step = cl_false;
202                 if (lm >= me) { // |y| >= 1/2 ?
203                         p = y_.sign; // 1 or -1
204                         lq = 1;
205                 } else {
206                         var uintL n = me - lm; // |y| < 2^-n with n maximal
207                         // Set p to the first n bits of |y|:
208                         if (lm > n) {
209                                 p = y_.mantissa >> (lm - n);
210                                 lq = 2*n;
211                         } else {
212                                 p = y_.mantissa;
213                                 lq = lm + n;
214                         }
215                         if (minusp(y_.sign)) { p = -p; }
216                         // If 2*n >= lm = intDsize*len, then within our
217                         // precision exp(-i*z')=1-i*z' (because |z'^2| < 2^-lm),
218                         // and we know a priori that the iteration will stop
219                         // after the next big multiplication. This saves one
220                         // big multiplication at the end.
221                         if (2*n >= lm)
222                                 last_step = cl_true;
223                 }
224                 z = z + scale_float(cl_I_to_LF(p,len),-(sintC)lq);
225                 if (last_step)
226                         break;
227                 var cl_LF_cos_sin_t cis_z = cl_cossin_aux(-p,lq,len);
228                 var cl_LF new_x = x*cis_z.cos - y*cis_z.sin;
229                 var cl_LF new_y = x*cis_z.sin + y*cis_z.cos;
230                 x = new_x; y = new_y;
231         }
232         return z+y;
233 }
234 // Bit complexity (N = length(x)): O(log(N)^2*M(N)).
235
236 // Timings of the above algorithms, on an i486 33 MHz, running Linux,
237 // applied to x = sqrt(2)-1 = 0.414...
238 //   N      naive1  naive2  ratseries
239 //   10     0.013   0.013   0.043
240 //   25     0.062   0.048   0.122
241 //   50     0.25    0.17    0.34
242 //  100     1.06    0.70    1.07
243 //  250     7.5     5.0     5.6
244 //  500    34.7    23.2    20.0
245 // 1000   167     112      65
246 // ==> ratseries faster for N >= 325.
247
248 const cl_F atanx (const cl_F& x)
249 {
250         if (longfloatp(x)) {
251                 DeclareType(cl_LF,x);
252                 if (TheLfloat(x)->len >= 325)
253                         return cl_float(atanx_ratseries(extend(x,TheLfloat(x)->len+1)),x);
254                 else
255                         return atanx_naive(x);
256         } else
257                 return atanx_naive(x);
258 }
259 // Bit complexity (N = length(x)): O(log(N)^2*M(N)).
260
261 }  // namespace cln