comparison libgfortran/intrinsics/trigd.inc @ 152:2b5abeee2509

update gcc11
author anatofuz
date Mon, 25 May 2020 07:50:57 +0900
parents
children
comparison
equal deleted inserted replaced
145:1830386684a0 152:2b5abeee2509
1 /* Implementation of the degree trignometric functions COSD, SIND, TAND.
2 Copyright (C) 2020 Free Software Foundation, Inc.
3 Contributed by Steven G. Kargl <kargl@gcc.gnu.org>
4 and Fritz Reese <foreese@gcc.gnu.org>
5
6 This file is part of the GNU Fortran runtime library (libgfortran).
7
8 Libgfortran is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public
10 License as published by the Free Software Foundation; either
11 version 3 of the License, or (at your option) any later version.
12
13 Libgfortran is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
21
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. */
26
27
28 /*
29
30 This file is included from both the FE and the runtime library code.
31 Operations are generalized using GMP/MPFR functions. When included from
32 libgfortran, these should be overridden using macros which will use native
33 operations conforming to the same API. From the FE, the GMP/MPFR functions can
34 be used as-is.
35
36 The following macros are used and must be defined, unless listed as [optional]:
37
38 FTYPE
39 Type name for the real-valued parameter.
40 Variables of this type are constructed/destroyed using mpfr_init()
41 and mpfr_clear.
42
43 RETTYPE
44 Return type of the functions.
45
46 RETURN(x)
47 Insert code to return a value.
48 The parameter x is the result variable, which was also the input parameter.
49
50 ITYPE
51 Type name for integer types.
52
53 SIND, COSD, TRIGD
54 Names for the degree-valued trig functions defined by this module.
55
56 ENABLE_SIND, ENABLE_COSD, ENABLE_TAND
57 Whether the degree-valued trig functions can be enabled.
58
59 ERROR_RETURN(f, k, x)
60 If ENABLE_<xxx>D is not defined, this is substituted to assert an
61 error condition for function f, kind k, and parameter x.
62 The function argument is one of {sind, cosd, tand}.
63
64 ISFINITE(x)
65 Whether x is a regular number or zero (not inf or NaN).
66
67 D2R(x)
68 Convert x from radians to degrees.
69
70 SET_COSD30(x)
71 Set x to COSD(30), or equivalently, SIND(60).
72
73 TINY_LITERAL [optional]
74 Value subtracted from 1 to cause raise INEXACT for COSD(x) for x << 1.
75 If not set, COSD(x) for x <= COSD_SMALL_LITERAL simply returns 1.
76
77 COSD_SMALL_LITERAL [optional]
78 Value such that x <= COSD_SMALL_LITERAL implies COSD(x) = 1 to within the
79 precision of FTYPE. If not set, this condition is not checked.
80
81 SIND_SMALL_LITERAL [optional]
82 Value such that x <= SIND_SMALL_LITERAL implies SIND(x) = D2R(x) to within
83 the precision of FTYPE. If not set, this condition is not checked.
84
85 */
86
87
88 #ifdef SIND
89 /* Compute sind(x) = sin(x * pi / 180). */
90
91 RETTYPE
92 SIND (FTYPE x)
93 {
94 #ifdef ENABLE_SIND
95 if (ISFINITE (x))
96 {
97 FTYPE s, one;
98
99 /* sin(-x) = - sin(x). */
100 mpfr_init (s);
101 mpfr_init_set_ui (one, 1, GFC_RND_MODE);
102 mpfr_copysign (s, one, x, GFC_RND_MODE);
103 mpfr_clear (one);
104
105 #ifdef SIND_SMALL_LITERAL
106 /* sin(x) = x as x -> 0; but only for some precisions. */
107 FTYPE ax;
108 mpfr_init (ax);
109 mpfr_abs (ax, x, GFC_RND_MODE);
110 if (mpfr_cmp_ld (ax, SIND_SMALL_LITERAL) < 0)
111 {
112 D2R (x);
113 mpfr_clear (ax);
114 return x;
115 }
116
117 mpfr_swap (x, ax);
118 mpfr_clear (ax);
119
120 #else
121 mpfr_abs (x, x, GFC_RND_MODE);
122 #endif /* SIND_SMALL_LITERAL */
123
124 /* Reduce angle to x in [0,360]. */
125 FTYPE period;
126 mpfr_init_set_ui (period, 360, GFC_RND_MODE);
127 mpfr_fmod (x, x, period, GFC_RND_MODE);
128 mpfr_clear (period);
129
130 /* Special cases with exact results. */
131 ITYPE n;
132 mpz_init (n);
133 if (mpfr_get_z (n, x, GFC_RND_MODE) == 0 && mpz_divisible_ui_p (n, 30))
134 {
135 /* Flip sign for odd n*pi (x is % 360 so this is only for 180).
136 This respects sgn(sin(x)) = sgn(d/dx sin(x)) = sgn(cos(x)). */
137 if (mpz_divisible_ui_p (n, 180))
138 {
139 mpfr_set_ui (x, 0, GFC_RND_MODE);
140 if (mpz_cmp_ui (n, 180) == 0)
141 mpfr_neg (s, s, GFC_RND_MODE);
142 }
143 else if (mpz_divisible_ui_p (n, 90))
144 mpfr_set_si (x, (mpz_cmp_ui (n, 90) == 0 ? 1 : -1), GFC_RND_MODE);
145 else if (mpz_divisible_ui_p (n, 60))
146 {
147 SET_COSD30 (x);
148 if (mpz_cmp_ui (n, 180) >= 0)
149 mpfr_neg (x, x, GFC_RND_MODE);
150 }
151 else
152 mpfr_set_ld (x, (mpz_cmp_ui (n, 180) < 0 ? 0.5L : -0.5L),
153 GFC_RND_MODE);
154 }
155
156 /* Fold [0,360] into the range [0,45], and compute either SIN() or
157 COS() depending on symmetry of shifting into the [0,45] range. */
158 else
159 {
160 bool fold_cos = false;
161 if (mpfr_cmp_ui (x, 180) <= 0)
162 {
163 if (mpfr_cmp_ui (x, 90) <= 0)
164 {
165 if (mpfr_cmp_ui (x, 45) > 0)
166 {
167 /* x = COS(D2R(90 - x)) */
168 mpfr_ui_sub (x, 90, x, GFC_RND_MODE);
169 fold_cos = true;
170 }
171 }
172 else
173 {
174 if (mpfr_cmp_ui (x, 135) <= 0)
175 {
176 mpfr_sub_ui (x, x, 90, GFC_RND_MODE);
177 fold_cos = true;
178 }
179 else
180 mpfr_ui_sub (x, 180, x, GFC_RND_MODE);
181 }
182 }
183
184 else if (mpfr_cmp_ui (x, 270) <= 0)
185 {
186 if (mpfr_cmp_ui (x, 225) <= 0)
187 mpfr_sub_ui (x, x, 180, GFC_RND_MODE);
188 else
189 {
190 mpfr_ui_sub (x, 270, x, GFC_RND_MODE);
191 fold_cos = true;
192 }
193 mpfr_neg (s, s, GFC_RND_MODE);
194 }
195
196 else
197 {
198 if (mpfr_cmp_ui (x, 315) <= 0)
199 {
200 mpfr_sub_ui (x, x, 270, GFC_RND_MODE);
201 fold_cos = true;
202 }
203 else
204 mpfr_ui_sub (x, 360, x, GFC_RND_MODE);
205 mpfr_neg (s, s, GFC_RND_MODE);
206 }
207
208 D2R (x);
209
210 if (fold_cos)
211 mpfr_cos (x, x, GFC_RND_MODE);
212 else
213 mpfr_sin (x, x, GFC_RND_MODE);
214 }
215
216 mpfr_mul (x, x, s, GFC_RND_MODE);
217 mpz_clear (n);
218 mpfr_clear (s);
219 }
220
221 /* Return NaN for +-Inf and NaN and raise exception. */
222 else
223 mpfr_sub (x, x, x, GFC_RND_MODE);
224
225 RETURN (x);
226
227 #else
228 ERROR_RETURN(sind, KIND, x);
229 #endif // ENABLE_SIND
230 }
231 #endif // SIND
232
233
234 #ifdef COSD
235 /* Compute cosd(x) = cos(x * pi / 180). */
236
237 RETTYPE
238 COSD (FTYPE x)
239 {
240 #ifdef ENABLE_COSD
241 #if defined(TINY_LITERAL) && defined(COSD_SMALL_LITERAL)
242 static const volatile FTYPE tiny = TINY_LITERAL;
243 #endif
244
245 if (ISFINITE (x))
246 {
247 #ifdef COSD_SMALL_LITERAL
248 FTYPE ax;
249 mpfr_init (ax);
250
251 mpfr_abs (ax, x, GFC_RND_MODE);
252 /* No spurious underflows!. In radians, cos(x) = 1-x*x/2 as x -> 0. */
253 if (mpfr_cmp_ld (ax, COSD_SMALL_LITERAL) <= 0)
254 {
255 mpfr_set_ui (x, 1, GFC_RND_MODE);
256 #ifdef TINY_LITERAL
257 /* Cause INEXACT. */
258 if (!mpfr_zero_p (ax))
259 mpfr_sub_d (x, x, tiny, GFC_RND_MODE);
260 #endif
261
262 mpfr_clear (ax);
263 return x;
264 }
265
266 mpfr_swap (x, ax);
267 mpfr_clear (ax);
268 #else
269 mpfr_abs (x, x, GFC_RND_MODE);
270 #endif /* COSD_SMALL_LITERAL */
271
272 /* Reduce angle to ax in [0,360]. */
273 FTYPE period;
274 mpfr_init_set_ui (period, 360, GFC_RND_MODE);
275 mpfr_fmod (x, x, period, GFC_RND_MODE);
276 mpfr_clear (period);
277
278 /* Special cases with exact results.
279 Return negative zero for cosd(270) for consistency with libm cos(). */
280 ITYPE n;
281 mpz_init (n);
282 if (mpfr_get_z (n, x, GFC_RND_MODE) == 0 && mpz_divisible_ui_p (n, 30))
283 {
284 if (mpz_divisible_ui_p (n, 180))
285 mpfr_set_si (x, (mpz_cmp_ui (n, 180) == 0 ? -1 : 1),
286 GFC_RND_MODE);
287 else if (mpz_divisible_ui_p (n, 90))
288 mpfr_set_zero (x, 0);
289 else if (mpz_divisible_ui_p (n, 60))
290 {
291 mpfr_set_ld (x, 0.5, GFC_RND_MODE);
292 if (mpz_cmp_ui (n, 60) != 0 && mpz_cmp_ui (n, 300) != 0)
293 mpfr_neg (x, x, GFC_RND_MODE);
294 }
295 else
296 {
297 SET_COSD30 (x);
298 if (mpz_cmp_ui (n, 30) != 0 && mpz_cmp_ui (n, 330) != 0)
299 mpfr_neg (x, x, GFC_RND_MODE);
300 }
301 }
302
303 /* Fold [0,360] into the range [0,45], and compute either SIN() or
304 COS() depending on symmetry of shifting into the [0,45] range. */
305 else
306 {
307 bool neg = false;
308 bool fold_sin = false;
309 if (mpfr_cmp_ui (x, 180) <= 0)
310 {
311 if (mpfr_cmp_ui (x, 90) <= 0)
312 {
313 if (mpfr_cmp_ui (x, 45) > 0)
314 {
315 mpfr_ui_sub (x, 90, x, GFC_RND_MODE);
316 fold_sin = true;
317 }
318 }
319 else
320 {
321 if (mpfr_cmp_ui (x, 135) <= 0)
322 {
323 mpfr_sub_ui (x, x, 90, GFC_RND_MODE);
324 fold_sin = true;
325 }
326 else
327 mpfr_ui_sub (x, 180, x, GFC_RND_MODE);
328 neg = true;
329 }
330 }
331
332 else if (mpfr_cmp_ui (x, 270) <= 0)
333 {
334 if (mpfr_cmp_ui (x, 225) <= 0)
335 mpfr_sub_ui (x, x, 180, GFC_RND_MODE);
336 else
337 {
338 mpfr_ui_sub (x, 270, x, GFC_RND_MODE);
339 fold_sin = true;
340 }
341 neg = true;
342 }
343
344 else
345 {
346 if (mpfr_cmp_ui (x, 315) <= 0)
347 {
348 mpfr_sub_ui (x, x, 270, GFC_RND_MODE);
349 fold_sin = true;
350 }
351 else
352 mpfr_ui_sub (x, 360, x, GFC_RND_MODE);
353 }
354
355 D2R (x);
356
357 if (fold_sin)
358 mpfr_sin (x, x, GFC_RND_MODE);
359 else
360 mpfr_cos (x, x, GFC_RND_MODE);
361
362 if (neg)
363 mpfr_neg (x, x, GFC_RND_MODE);
364 }
365
366 mpz_clear (n);
367 }
368
369 /* Return NaN for +-Inf and NaN and raise exception. */
370 else
371 mpfr_sub (x, x, x, GFC_RND_MODE);
372
373 RETURN (x);
374
375 #else
376 ERROR_RETURN(cosd, KIND, x);
377 #endif // ENABLE_COSD
378 }
379 #endif // COSD
380
381
382 #ifdef TAND
383 /* Compute tand(x) = tan(x * pi / 180). */
384
385 RETTYPE
386 TAND (FTYPE x)
387 {
388 #ifdef ENABLE_TAND
389 if (ISFINITE (x))
390 {
391 FTYPE s, one;
392
393 /* tan(-x) = - tan(x). */
394 mpfr_init (s);
395 mpfr_init_set_ui (one, 1, GFC_RND_MODE);
396 mpfr_copysign (s, one, x, GFC_RND_MODE);
397 mpfr_clear (one);
398
399 #ifdef SIND_SMALL_LITERAL
400 /* tan(x) = x as x -> 0; but only for some precisions. */
401 FTYPE ax;
402 mpfr_init (ax);
403 mpfr_abs (ax, x, GFC_RND_MODE);
404 if (mpfr_cmp_ld (ax, SIND_SMALL_LITERAL) < 0)
405 {
406 D2R (x);
407 mpfr_clear (ax);
408 return x;
409 }
410
411 mpfr_swap (x, ax);
412 mpfr_clear (ax);
413
414 #else
415 mpfr_abs (x, x, GFC_RND_MODE);
416 #endif /* SIND_SMALL_LITERAL */
417
418 /* Reduce angle to x in [0,360]. */
419 FTYPE period;
420 mpfr_init_set_ui (period, 360, GFC_RND_MODE);
421 mpfr_fmod (x, x, period, GFC_RND_MODE);
422 mpfr_clear (period);
423
424 /* Special cases with exact results. */
425 ITYPE n;
426 mpz_init (n);
427 if (mpfr_get_z (n, x, GFC_RND_MODE) == 0 && mpz_divisible_ui_p (n, 45))
428 {
429 if (mpz_divisible_ui_p (n, 180))
430 mpfr_set_zero (x, 0);
431
432 /* Though mathematically NaN is more appropriate for tan(n*90),
433 returning +/-Inf offers the advantage that 1/tan(n*90) returns 0,
434 which is mathematically sound. In fact we rely on this behavior
435 to implement COTAND(x) = 1 / TAND(x).
436 */
437 else if (mpz_divisible_ui_p (n, 90))
438 mpfr_set_inf (x, mpz_cmp_ui (n, 90) == 0 ? 0 : 1);
439
440 else
441 {
442 mpfr_set_ui (x, 1, GFC_RND_MODE);
443 if (mpz_cmp_ui (n, 45) != 0 && mpz_cmp_ui (n, 225) != 0)
444 mpfr_neg (x, x, GFC_RND_MODE);
445 }
446 }
447
448 else
449 {
450 /* Fold [0,360] into the range [0,90], and compute TAN(). */
451 if (mpfr_cmp_ui (x, 180) <= 0)
452 {
453 if (mpfr_cmp_ui (x, 90) > 0)
454 {
455 mpfr_ui_sub (x, 180, x, GFC_RND_MODE);
456 mpfr_neg (s, s, GFC_RND_MODE);
457 }
458 }
459 else
460 {
461 if (mpfr_cmp_ui (x, 270) <= 0)
462 {
463 mpfr_sub_ui (x, x, 180, GFC_RND_MODE);
464 }
465 else
466 {
467 mpfr_ui_sub (x, 360, x, GFC_RND_MODE);
468 mpfr_neg (s, s, GFC_RND_MODE);
469 }
470 }
471
472 D2R (x);
473 mpfr_tan (x, x, GFC_RND_MODE);
474 }
475
476 mpfr_mul (x, x, s, GFC_RND_MODE);
477 mpz_clear (n);
478 mpfr_clear (s);
479 }
480
481 /* Return NaN for +-Inf and NaN and raise exception. */
482 else
483 mpfr_sub (x, x, x, GFC_RND_MODE);
484
485 RETURN (x);
486
487 #else
488 ERROR_RETURN(tand, KIND, x);
489 #endif // ENABLE_TAND
490 }
491 #endif // TAND
492
493 /* vim: set ft=c: */