]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/ieee754/dbl-64/sincos32.c
Consolidate multiple precision sin/cos functions
[thirdparty/glibc.git] / sysdeps / ieee754 / dbl-64 / sincos32.c
CommitLineData
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
53static void
54SECTION
97a0650b
SP
55ss32 (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
79static void
80SECTION
97a0650b
SP
81cc32 (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
105void
106SECTION
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
130double
131SECTION
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
158double
159SECTION
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
193double
194SECTION
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
251double
252SECTION
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
309int
310SECTION
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}