trigd.inc 12 KB

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