]>
Commit | Line | Data |
---|---|---|
e4d82761 UD |
1 | /* |
2 | * IBM Accurate Mathematical Library | |
aeb25823 | 3 | * written by International Business Machines Corp. |
568035b7 | 4 | * Copyright (C) 2001-2013 Free Software Foundation, Inc. |
e4d82761 UD |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU Lesser General Public License as published by | |
cc7375ce | 8 | * the Free Software Foundation; either version 2.1 of the License, or |
e4d82761 | 9 | * (at your option) any later version. |
50944bca | 10 | * |
e4d82761 UD |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
c6c6dd48 | 14 | * GNU Lesser General Public License for more details. |
e4d82761 UD |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public License | |
59ba27a6 | 17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
e4d82761 UD |
18 | */ |
19 | /****************************************************************/ | |
20 | /* MODULE_NAME: sincos32.c */ | |
21 | /* */ | |
22 | /* FUNCTIONS: ss32 */ | |
23 | /* cc32 */ | |
24 | /* c32 */ | |
25 | /* sin32 */ | |
26 | /* cos32 */ | |
27 | /* mpsin */ | |
28 | /* mpcos */ | |
29 | /* mpranred */ | |
30 | /* mpsin1 */ | |
31 | /* mpcos1 */ | |
32 | /* */ | |
33 | /* FILES NEEDED: endian.h mpa.h sincos32.h */ | |
34 | /* mpa.c */ | |
35 | /* */ | |
36 | /* Multi Precision sin() and cos() function with p=32 for sin()*/ | |
37 | /* cos() arcsin() and arccos() routines */ | |
38 | /* In addition mpranred() routine performs range reduction of */ | |
39 | /* a double number x into multi precision number y, */ | |
40 | /* such that y=x-n*pi/2, abs(y)<pi/4, n=0,+-1,+-2,.... */ | |
41 | /****************************************************************/ | |
42 | #include "endian.h" | |
43 | #include "mpa.h" | |
44 | #include "sincos32.h" | |
1ed0291c | 45 | #include <math_private.h> |
e4d82761 | 46 | |
31d3cc00 UD |
47 | #ifndef SECTION |
48 | # define SECTION | |
49 | #endif | |
50 | ||
97a0650b SP |
51 | /* Compute Multi-Precision sin() function for given p. Receive Multi Precision |
52 | number x and result stored at y. */ | |
31d3cc00 UD |
53 | static void |
54 | SECTION | |
97a0650b SP |
55 | ss32 (mp_no *x, mp_no *y, int p) |
56 | { | |
e4d82761 | 57 | int i; |
50944bca | 58 | double a; |
97a0650b SP |
59 | mp_no mpt1, x2, gor, sum, mpk = {1, {1.0}}; |
60 | for (i = 1; i <= p; i++) | |
61 | mpk.d[i] = 0; | |
e4d82761 | 62 | |
97a0650b SP |
63 | __sqr (x, &x2, p); |
64 | __cpy (&oofac27, &gor, p); | |
65 | __cpy (&gor, &sum, p); | |
66 | for (a = 27.0; a > 1.0; a -= 2.0) | |
67 | { | |
68 | mpk.d[1] = a * (a - 1.0); | |
69 | __mul (&gor, &mpk, &mpt1, p); | |
70 | __cpy (&mpt1, &gor, p); | |
71 | __mul (&x2, &sum, &mpt1, p); | |
72 | __sub (&gor, &mpt1, &sum, p); | |
73 | } | |
74 | __mul (x, &sum, y, p); | |
e4d82761 UD |
75 | } |
76 | ||
97a0650b SP |
77 | /* Compute Multi-Precision cos() function for given p. Receive Multi Precision |
78 | number x and result stored at y. */ | |
31d3cc00 UD |
79 | static void |
80 | SECTION | |
97a0650b SP |
81 | cc32 (mp_no *x, mp_no *y, int p) |
82 | { | |
e4d82761 | 83 | int i; |
50944bca | 84 | double a; |
97a0650b SP |
85 | mp_no mpt1, x2, gor, sum, mpk = {1, {1.0}}; |
86 | for (i = 1; i <= p; i++) | |
87 | mpk.d[i] = 0; | |
e4d82761 | 88 | |
97a0650b SP |
89 | __sqr (x, &x2, p); |
90 | mpk.d[1] = 27.0; | |
91 | __mul (&oofac27, &mpk, &gor, p); | |
92 | __cpy (&gor, &sum, p); | |
93 | for (a = 26.0; a > 2.0; a -= 2.0) | |
94 | { | |
95 | mpk.d[1] = a * (a - 1.0); | |
96 | __mul (&gor, &mpk, &mpt1, p); | |
97 | __cpy (&mpt1, &gor, p); | |
98 | __mul (&x2, &sum, &mpt1, p); | |
99 | __sub (&gor, &mpt1, &sum, p); | |
100 | } | |
101 | __mul (&x2, &sum, y, p); | |
e4d82761 UD |
102 | } |
103 | ||
97a0650b | 104 | /* Compute both sin(x), cos(x) as Multi precision numbers. */ |
31d3cc00 UD |
105 | void |
106 | SECTION | |
97a0650b SP |
107 | __c32 (mp_no *x, mp_no *y, mp_no *z, int p) |
108 | { | |
109 | mp_no u, t, t1, t2, c, s; | |
e4d82761 | 110 | int i; |
97a0650b SP |
111 | __cpy (x, &u, p); |
112 | u.e = u.e - 1; | |
113 | cc32 (&u, &c, p); | |
114 | ss32 (&u, &s, p); | |
115 | for (i = 0; i < 24; i++) | |
116 | { | |
117 | __mul (&c, &s, &t, p); | |
118 | __sub (&s, &t, &t1, p); | |
119 | __add (&t1, &t1, &s, p); | |
120 | __sub (&mptwo, &c, &t1, p); | |
121 | __mul (&t1, &c, &t2, p); | |
122 | __add (&t2, &t2, &c, p); | |
123 | } | |
124 | __sub (&mpone, &c, y, p); | |
125 | __cpy (&s, z, p); | |
e4d82761 UD |
126 | } |
127 | ||
97a0650b SP |
128 | /* Receive double x and two double results of sin(x) and return result which is |
129 | more accurate, computing sin(x) with multi precision routine c32. */ | |
31d3cc00 UD |
130 | double |
131 | SECTION | |
97a0650b SP |
132 | __sin32 (double x, double res, double res1) |
133 | { | |
e4d82761 | 134 | int p; |
97a0650b SP |
135 | mp_no a, b, c; |
136 | p = 32; | |
137 | __dbl_mp (res, &a, p); | |
138 | __dbl_mp (0.5 * (res1 - res), &b, p); | |
139 | __add (&a, &b, &c, p); | |
140 | if (x > 0.8) | |
141 | { | |
142 | __sub (&hp, &c, &a, p); | |
143 | __c32 (&a, &b, &c, p); | |
144 | } | |
145 | else | |
146 | __c32 (&c, &a, &b, p); /* b=sin(0.5*(res+res1)) */ | |
147 | __dbl_mp (x, &c, p); /* c = x */ | |
148 | __sub (&b, &c, &a, p); | |
149 | /* if a > 0 return min (res, res1), otherwise return max (res, res1). */ | |
150 | if (a.d[0] > 0) | |
151 | return (res < res1) ? res : res1; | |
152 | else | |
153 | return (res > res1) ? res : res1; | |
e4d82761 UD |
154 | } |
155 | ||
97a0650b SP |
156 | /* Receive double x and two double results of cos(x) and return result which is |
157 | more accurate, computing cos(x) with multi precision routine c32. */ | |
31d3cc00 UD |
158 | double |
159 | SECTION | |
97a0650b SP |
160 | __cos32 (double x, double res, double res1) |
161 | { | |
e4d82761 | 162 | int p; |
97a0650b SP |
163 | mp_no a, b, c; |
164 | p = 32; | |
165 | __dbl_mp (res, &a, p); | |
166 | __dbl_mp (0.5 * (res1 - res), &b, p); | |
167 | __add (&a, &b, &c, p); | |
168 | if (x > 2.4) | |
169 | { | |
170 | __sub (&pi, &c, &a, p); | |
171 | __c32 (&a, &b, &c, p); | |
172 | b.d[0] = -b.d[0]; | |
173 | } | |
174 | else if (x > 0.8) | |
175 | { | |
176 | __sub (&hp, &c, &a, p); | |
177 | __c32 (&a, &c, &b, p); | |
178 | } | |
179 | else | |
180 | __c32 (&c, &b, &a, p); /* b=cos(0.5*(res+res1)) */ | |
181 | __dbl_mp (x, &c, p); /* c = x */ | |
182 | __sub (&b, &c, &a, p); | |
183 | /* if a > 0 return max (res, res1), otherwise return min (res, res1). */ | |
184 | if (a.d[0] > 0) | |
185 | return (res > res1) ? res : res1; | |
186 | else | |
187 | return (res < res1) ? res : res1; | |
e4d82761 UD |
188 | } |
189 | ||
09544cbc SP |
190 | /* Compute sin() of double-length number (X + DX) as Multi Precision number and |
191 | return result as double. If REDUCE_RANGE is true, X is assumed to be the | |
192 | original input and DX is ignored. */ | |
31d3cc00 UD |
193 | double |
194 | SECTION | |
09544cbc | 195 | __mpsin (double x, double dx, bool reduce_range) |
97a0650b | 196 | { |
e4d82761 | 197 | double y; |
09544cbc SP |
198 | mp_no a, b, c, s; |
199 | int n; | |
200 | int p = 32; | |
201 | ||
202 | if (reduce_range) | |
97a0650b | 203 | { |
09544cbc SP |
204 | n = __mpranred (x, &a, p); /* n is 0, 1, 2 or 3. */ |
205 | __c32 (&a, &c, &s, p); | |
97a0650b SP |
206 | } |
207 | else | |
09544cbc SP |
208 | { |
209 | n = -1; | |
210 | __dbl_mp (x, &b, p); | |
211 | __dbl_mp (dx, &c, p); | |
212 | __add (&b, &c, &a, p); | |
213 | if (x > 0.8) | |
214 | { | |
215 | __sub (&hp, &a, &b, p); | |
216 | __c32 (&b, &s, &c, p); | |
217 | } | |
218 | else | |
219 | __c32 (&a, &c, &s, p); /* b = sin(x+dx) */ | |
220 | } | |
221 | ||
222 | /* Convert result based on which quarter of unit circle y is in. */ | |
223 | switch (n) | |
224 | { | |
225 | case 1: | |
226 | __mp_dbl (&c, &y, p); | |
227 | break; | |
228 | ||
229 | case 3: | |
230 | __mp_dbl (&c, &y, p); | |
231 | y = -y; | |
232 | break; | |
233 | ||
234 | case 2: | |
235 | __mp_dbl (&s, &y, p); | |
236 | y = -y; | |
237 | break; | |
238 | ||
239 | /* Quadrant not set, so the result must be sin (X + DX), which is also in | |
240 | S. */ | |
241 | case 0: | |
242 | default: | |
243 | __mp_dbl (&s, &y, p); | |
244 | } | |
e4d82761 UD |
245 | return y; |
246 | } | |
247 | ||
09544cbc SP |
248 | /* Compute cos() of double-length number (X + DX) as Multi Precision number and |
249 | return result as double. If REDUCE_RANGE is true, X is assumed to be the | |
250 | original input and DX is ignored. */ | |
31d3cc00 UD |
251 | double |
252 | SECTION | |
09544cbc | 253 | __mpcos (double x, double dx, bool reduce_range) |
97a0650b | 254 | { |
e4d82761 | 255 | double y; |
09544cbc SP |
256 | mp_no a, b, c, s; |
257 | int n; | |
258 | int p = 32; | |
259 | ||
260 | if (reduce_range) | |
97a0650b | 261 | { |
09544cbc SP |
262 | n = __mpranred (x, &a, p); /* n is 0, 1, 2 or 3. */ |
263 | __c32 (&a, &c, &s, p); | |
97a0650b SP |
264 | } |
265 | else | |
09544cbc SP |
266 | { |
267 | n = -1; | |
268 | __dbl_mp (x, &b, p); | |
269 | __dbl_mp (dx, &c, p); | |
270 | __add (&b, &c, &a, p); | |
271 | if (x > 0.8) | |
272 | { | |
273 | __sub (&hp, &a, &b, p); | |
274 | __c32 (&b, &s, &c, p); | |
275 | } | |
276 | else | |
277 | __c32 (&a, &c, &s, p); /* a = cos(x+dx) */ | |
278 | } | |
279 | ||
280 | /* Convert result based on which quarter of unit circle y is in. */ | |
281 | switch (n) | |
282 | { | |
283 | case 1: | |
284 | __mp_dbl (&s, &y, p); | |
285 | y = -y; | |
286 | break; | |
287 | ||
288 | case 3: | |
289 | __mp_dbl (&s, &y, p); | |
290 | break; | |
291 | ||
292 | case 2: | |
293 | __mp_dbl (&c, &y, p); | |
294 | y = -y; | |
295 | break; | |
296 | ||
297 | /* Quadrant not set, so the result must be cos (X + DX), which is also | |
298 | stored in C. */ | |
299 | case 0: | |
300 | default: | |
301 | __mp_dbl (&c, &y, p); | |
302 | } | |
e4d82761 UD |
303 | return y; |
304 | } | |
305 | ||
97a0650b SP |
306 | /* Perform range reduction of a double number x into multi precision number y, |
307 | such that y = x - n * pi / 2, abs (y) < pi / 4, n = 0, +-1, +-2, ... | |
308 | Return int which indicates in which quarter of circle x is. */ | |
31d3cc00 UD |
309 | int |
310 | SECTION | |
97a0650b | 311 | __mpranred (double x, mp_no *y, int p) |
e4d82761 UD |
312 | { |
313 | number v; | |
97a0650b SP |
314 | double t, xn; |
315 | int i, k, n; | |
316 | mp_no a, b, c; | |
50944bca | 317 | |
97a0650b SP |
318 | if (ABS (x) < 2.8e14) |
319 | { | |
320 | t = (x * hpinv.d + toint.d); | |
321 | xn = t - toint.d; | |
322 | v.d = t; | |
323 | n = v.i[LOW_HALF] & 3; | |
324 | __dbl_mp (xn, &a, p); | |
325 | __mul (&a, &hp, &b, p); | |
326 | __dbl_mp (x, &c, p); | |
327 | __sub (&c, &b, y, p); | |
328 | return n; | |
329 | } | |
330 | else | |
331 | { | |
332 | /* If x is very big more precision required. */ | |
333 | __dbl_mp (x, &a, p); | |
334 | a.d[0] = 1.0; | |
335 | k = a.e - 5; | |
336 | if (k < 0) | |
337 | k = 0; | |
338 | b.e = -k; | |
339 | b.d[0] = 1.0; | |
340 | for (i = 0; i < p; i++) | |
341 | b.d[i + 1] = toverp[i + k]; | |
342 | __mul (&a, &b, &c, p); | |
343 | t = c.d[c.e]; | |
344 | for (i = 1; i <= p - c.e; i++) | |
345 | c.d[i] = c.d[i + c.e]; | |
346 | for (i = p + 1 - c.e; i <= p; i++) | |
347 | c.d[i] = 0; | |
348 | c.e = 0; | |
349 | if (c.d[1] >= HALFRAD) | |
350 | { | |
351 | t += 1.0; | |
352 | __sub (&c, &mpone, &b, p); | |
353 | __mul (&b, &hp, y, p); | |
354 | } | |
355 | else | |
356 | __mul (&c, &hp, y, p); | |
357 | n = (int) t; | |
358 | if (x < 0) | |
359 | { | |
360 | y->d[0] = -y->d[0]; | |
361 | n = -n; | |
362 | } | |
363 | return (n & 3); | |
e4d82761 | 364 | } |
e4d82761 | 365 | } |