08f4192998d88445cc18c635907215bcb6cba696
[platform/upstream/coreclr.git] / src / pal / src / cruntime / math.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 /*++
6
7
8
9 Module Name:
10
11     math.cpp
12
13 Abstract:
14
15     Implementation of math family functions.
16
17
18
19 --*/
20
21 #include "pal/palinternal.h"
22 #include "pal/dbgmsg.h"
23
24 #include <math.h>
25
26 #if HAVE_IEEEFP_H
27 #include <ieeefp.h>
28 #endif  // HAVE_IEEEFP_H
29
30 #include <errno.h>
31
32 #define PAL_NAN_DBL     sqrt(-1.0)
33 #define PAL_POSINF_DBL -log(0.0)
34 #define PAL_NEGINF_DBL  log(0.0)
35
36 #define IS_DBL_NEGZERO(x)         (((*((INT64*)((void*)&x))) & I64(0xFFFFFFFFFFFFFFFF)) == I64(0x8000000000000000))
37
38 #define PAL_NAN_FLT     sqrtf(-1.0f)
39 #define PAL_POSINF_FLT -logf(0.0f)
40 #define PAL_NEGINF_FLT  logf(0.0f)
41
42 #define IS_FLT_NEGZERO(x)         (((*((INT32*)((void*)&x))) & 0xFFFFFFFF) == 0x80000000)
43
44 SET_DEFAULT_DEBUG_CHANNEL(CRT);
45
46 /*++
47 Function:
48   _finite
49
50 Determines whether given double-precision floating point value is finite.
51
52 Return Value
53
54 _finite returns a nonzero value (TRUE) if its argument x is not
55 infinite, that is, if -INF < x < +INF. It returns 0 (FALSE) if the
56 argument is infinite or a NaN.
57
58 Parameter
59
60 x  Double-precision floating-point value
61
62 --*/
63 int __cdecl _finite(double x)
64 {
65     int ret;
66     PERF_ENTRY(_finite);
67     ENTRY("_finite (x=%f)\n", x);
68
69 #if defined(_IA64_) && defined (_HPUX_)
70     ret = !isnan(x) && (x != PAL_POSINF_DBL) && (x != PAL_NEGINF_DBL);
71 #else
72     ret = isfinite(x);
73 #endif
74
75     LOGEXIT("_finite returns int %d\n", ret);
76     PERF_EXIT(_finite);
77     return ret;
78 }
79
80 /*++
81 Function:
82   _isnan
83
84 See MSDN doc
85 --*/
86 int __cdecl _isnan(double x)
87 {
88     int ret;
89     PERF_ENTRY(_isnan);
90     ENTRY("_isnan (x=%f)\n", x);
91
92     ret = isnan(x);
93
94     LOGEXIT("_isnan returns int %d\n", ret);
95     PERF_EXIT(_isnan);
96     return ret;
97 }
98
99 /*++
100 Function:
101   _copysign
102
103 See MSDN doc
104 --*/
105 double __cdecl _copysign(double x, double y)
106 {
107     double ret;
108     PERF_ENTRY(_copysign);
109     ENTRY("_copysign (x=%f, y=%f)\n", x, y);
110
111     ret = copysign(x, y);
112
113     LOGEXIT("_copysign returns double %f\n", ret);
114     PERF_EXIT(_copysign);
115     return ret;
116 }
117
118 /*++
119 Function:
120     acos
121
122 See MSDN.
123 --*/
124 PALIMPORT double __cdecl PAL_acos(double x)
125 {
126     double ret;
127     PERF_ENTRY(acos);
128     ENTRY("acos (x=%f)\n", x);
129
130 #if !HAVE_COMPATIBLE_ACOS
131     errno = 0;
132 #endif  // HAVE_COMPATIBLE_ACOS
133
134     ret = acos(x);
135     
136 #if !HAVE_COMPATIBLE_ACOS
137     if (errno == EDOM)
138     {
139         ret = PAL_NAN_DBL;  // NaN
140     }
141 #endif  // HAVE_COMPATIBLE_ACOS
142
143     LOGEXIT("acos returns double %f\n", ret);
144     PERF_EXIT(acos);
145     return ret;
146 }
147
148 /*++
149 Function:
150     asin
151
152 See MSDN.
153 --*/
154 PALIMPORT double __cdecl PAL_asin(double x)
155 {
156     double ret;
157     PERF_ENTRY(asin);
158     ENTRY("asin (x=%f)\n", x);
159
160 #if !HAVE_COMPATIBLE_ASIN
161     errno = 0;
162 #endif  // HAVE_COMPATIBLE_ASIN
163
164     ret = asin(x);
165
166 #if !HAVE_COMPATIBLE_ASIN
167     if (errno == EDOM)
168     {
169         ret = PAL_NAN_DBL;  // NaN
170     }
171 #endif  // HAVE_COMPATIBLE_ASIN
172
173     LOGEXIT("asin returns double %f\n", ret);
174     PERF_EXIT(asin);
175     return ret;
176 }
177
178 /*++
179 Function:
180     atan2
181
182 See MSDN.
183 --*/
184 PALIMPORT double __cdecl PAL_atan2(double y, double x)
185 {
186     double ret;
187     PERF_ENTRY(atan2);
188     ENTRY("atan2 (y=%f, x=%f)\n", y, x);
189
190 #if !HAVE_COMPATIBLE_ATAN2
191     errno = 0;
192 #endif  // !HAVE_COMPATIBLE_ATAN2
193
194     ret = atan2(y, x);
195
196 #if !HAVE_COMPATIBLE_ATAN2
197     if ((errno == EDOM) && (x == 0.0) && (y == 0.0))
198     {
199         const double sign_x = copysign(1.0, x);
200         const double sign_y = copysign(1.0, y);
201
202         if (sign_x > 0)
203         {
204             ret = copysign(0.0, sign_y);
205         }
206         else
207         {
208             ret = copysign(atan2(0.0, -1.0), sign_y);
209         }
210     }
211 #endif  // !HAVE_COMPATIBLE_ATAN2
212
213     LOGEXIT("atan2 returns double %f\n", ret);
214     PERF_EXIT(atan2);
215     return ret;
216 }
217
218 /*++
219 Function:
220     exp
221
222 See MSDN.
223 --*/
224 PALIMPORT double __cdecl PAL_exp(double x)
225 {
226     double ret;
227     PERF_ENTRY(exp);
228     ENTRY("exp (x=%f)\n", x);
229
230 #if !HAVE_COMPATIBLE_EXP
231     if (x == 1.0) 
232     {
233         ret = M_E;
234     }
235     else
236     {
237 #endif  // HAVE_COMPATIBLE_EXP
238
239     ret = exp(x);
240     
241 #if !HAVE_COMPATIBLE_EXP
242     }
243 #endif // HAVE_COMPATIBLE_EXP
244
245     LOGEXIT("exp returns double %f\n", ret);
246     PERF_EXIT(exp);
247     return ret;
248 }
249
250 /*++
251 Function:
252     labs
253
254 See MSDN.
255 --*/
256 PALIMPORT LONG __cdecl PAL_labs(LONG l)
257 {
258     long lRet;
259     PERF_ENTRY(labs);
260     ENTRY("labs (l=%ld)\n", l);
261     
262     lRet = labs(l);    
263
264     LOGEXIT("labs returns long %ld\n", lRet);
265     PERF_EXIT(labs);
266     return (LONG)lRet; // This explicit cast to LONG is used to silence any potential warnings due to implicitly casting the native long lRet to LONG when returning.
267 }
268
269 /*++
270 Function:
271     log
272
273 See MSDN.
274 --*/
275 PALIMPORT double __cdecl PAL_log(double x)
276 {
277     double ret;
278     PERF_ENTRY(log);
279     ENTRY("log (x=%f)\n", x);
280
281 #if !HAVE_COMPATIBLE_LOG
282     errno = 0;
283 #endif  // !HAVE_COMPATIBLE_LOG
284
285     ret = log(x);
286
287 #if !HAVE_COMPATIBLE_LOG
288     if ((errno == EDOM) && (x < 0))
289     {
290         ret = PAL_NAN_DBL;    // NaN
291     }
292 #endif  // !HAVE_COMPATIBLE_LOG
293
294     LOGEXIT("log returns double %f\n", ret);
295     PERF_EXIT(log);
296     return ret;
297 }
298
299 /*++
300 Function:
301     log10
302
303 See MSDN.
304 --*/
305 PALIMPORT double __cdecl PAL_log10(double x)
306 {
307     double ret;
308     PERF_ENTRY(log10);
309     ENTRY("log10 (x=%f)\n", x);
310
311 #if !HAVE_COMPATIBLE_LOG10
312     errno = 0;
313 #endif  // !HAVE_COMPATIBLE_LOG10
314
315     ret = log10(x);
316     
317 #if !HAVE_COMPATIBLE_LOG10
318     if ((errno == EDOM) && (x < 0))
319     {
320         ret = PAL_NAN_DBL;    // NaN
321     }
322 #endif  // !HAVE_COMPATIBLE_LOG10
323
324     LOGEXIT("log10 returns double %f\n", ret);
325     PERF_EXIT(log10);
326     return ret;
327 }
328
329 /*++
330 Function:
331     pow
332
333 See MSDN.
334 --*/
335 PALIMPORT double __cdecl PAL_pow(double x, double y)
336 {
337     double ret;
338     PERF_ENTRY(pow);
339     ENTRY("pow (x=%f, y=%f)\n", x, y);
340
341 #if !HAVE_COMPATIBLE_POW
342     if ((y == PAL_POSINF_DBL) && !isnan(x))    // +Inf
343     {
344         if (x == 1.0)
345         {
346             ret = x;
347         }
348         else if (x == -1.0)
349         {
350             ret = PAL_NAN_DBL;    // NaN
351         }
352         else if ((x > -1.0) && (x < 1.0))
353         {
354             ret = 0.0;
355         }
356         else
357         {
358             ret = PAL_POSINF_DBL;    // +Inf
359         }
360     }
361     else if ((y == PAL_NEGINF_DBL) && !isnan(x))   // -Inf
362     {
363         if (x == 1.0)
364         {
365             ret = x;
366         }
367         else if (x == -1.0)
368         {
369             ret = PAL_NAN_DBL;    // NaN
370         }
371         else if ((x > -1.0) && (x < 1.0))
372         {
373             ret = PAL_POSINF_DBL;    // +Inf
374         }
375         else
376         {
377             ret = 0.0;
378         }
379     }
380     else if (IS_DBL_NEGZERO(x) && (y == -1.0))
381     {
382         ret = PAL_NEGINF_DBL;    // -Inf
383     }
384     else if ((x == 0.0) && (y < 0.0))
385     {
386         ret = PAL_POSINF_DBL;    // +Inf
387     }
388     else
389 #endif  // !HAVE_COMPATIBLE_POW
390
391     if ((y == 0.0) && isnan(x))
392     {
393         // Windows returns NaN for pow(NaN, 0), but POSIX specifies
394         // a return value of 1 for that case.  We need to return
395         // the same result as Windows.
396         ret = PAL_NAN_DBL;
397     }
398     else
399     {
400         ret = pow(x, y);
401     }
402
403 #if !HAVE_VALID_NEGATIVE_INF_POW
404     if ((ret == PAL_POSINF_DBL) && (x < 0) && isfinite(x) && (ceil(y / 2) != floor(y / 2)))
405     {
406         ret = PAL_NEGINF_DBL;   // -Inf
407     }
408 #endif  // !HAVE_VALID_NEGATIVE_INF_POW
409
410 #if !HAVE_VALID_POSITIVE_INF_POW
411     /*
412     * The even/odd test in the if (this one and the one above) used to be ((long long) y % 2 == 0)
413     * on SPARC (long long) y for large y (>2**63) is always 0x7fffffff7fffffff, which
414     * is an odd number, so the test ((long long) y % 2 == 0) will always fail for
415     * large y. Since large double numbers are always even (e.g., the representation of
416     * 1E20+1 is the same as that of 1E20, the last .+1. is too insignificant to be part
417     * of the representation), this test will always return the wrong result for large y.
418     * 
419     * The (ceil(y/2) == floor(y/2)) test is slower, but more robust.
420     */
421     if ((ret == PAL_NEGINF_DBL) && (x < 0) && isfinite(x) && (ceil(y / 2) == floor(y / 2)))
422     {
423         ret = PAL_POSINF_DBL;   // +Inf
424     }
425 #endif  // !HAVE_VALID_POSITIVE_INF_POW
426
427     LOGEXIT("pow returns double %f\n", ret);
428     PERF_EXIT(pow);
429     return ret;
430 }
431
432 /*++
433 Function:
434   _finitef
435
436 Determines whether given single-precision floating point value is finite.
437
438 Return Value
439
440 _finitef returns a nonzero value (TRUE) if its argument x is not
441 infinite, that is, if -INF < x < +INF. It returns 0 (FALSE) if the
442 argument is infinite or a NaN.
443
444 Parameter
445
446 x  Single-precision floating-point value
447
448 --*/
449 int __cdecl _finitef(float x)
450 {
451     int ret;
452     PERF_ENTRY(_finitef);
453     ENTRY("_finitef (x=%f)\n", x);
454
455 #if defined(_IA64_) && defined (_HPUX_)
456     ret = !isnan(x) && (x != PAL_POSINF_FLT) && (x != PAL_NEGINF_FLT);
457 #else
458     ret = isfinite(x);
459 #endif
460
461     LOGEXIT("_finitef returns int %d\n", ret);
462     PERF_EXIT(_finitef);
463     return ret;
464 }
465
466 /*++
467 Function:
468   _isnanf
469
470 See MSDN doc
471 --*/
472 int __cdecl _isnanf(float x)
473 {
474     int ret;
475     PERF_ENTRY(_isnanf);
476     ENTRY("_isnanf (x=%f)\n", x);
477
478     ret = isnan(x);
479
480     LOGEXIT("_isnanf returns int %d\n", ret);
481     PERF_EXIT(_isnanf);
482     return ret;
483 }
484
485 /*++
486 Function:
487   _copysignf
488
489 See MSDN doc
490 --*/
491 float __cdecl _copysignf(float x, float y)
492 {
493     float ret;
494     PERF_ENTRY(_copysignf);
495     ENTRY("_copysignf (x=%f, y=%f)\n", x, y);
496
497     ret = copysign(x, y);
498
499     LOGEXIT("_copysignf returns float %f\n", ret);
500     PERF_EXIT(_copysignf);
501     return ret;
502 }
503
504 /*++
505 Function:
506     acosf
507
508 See MSDN.
509 --*/
510 PALIMPORT float __cdecl PAL_acosf(float x)
511 {
512     float ret;
513     PERF_ENTRY(acosf);
514     ENTRY("acosf (x=%f)\n", x);
515
516 #if !HAVE_COMPATIBLE_ACOS
517     errno = 0;
518 #endif  // HAVE_COMPATIBLE_ACOS
519
520     ret = acosf(x);
521     
522 #if !HAVE_COMPATIBLE_ACOS
523     if (errno == EDOM)
524     {
525         ret = PAL_NAN_FLT;  // NaN
526     }
527 #endif  // HAVE_COMPATIBLE_ACOS
528
529     LOGEXIT("acosf returns float %f\n", ret);
530     PERF_EXIT(acosf);
531     return ret;
532 }
533
534 /*++
535 Function:
536     asinf
537
538 See MSDN.
539 --*/
540 PALIMPORT float __cdecl PAL_asinf(float x)
541 {
542     float ret;
543     PERF_ENTRY(asinf);
544     ENTRY("asinf (x=%f)\n", x);
545
546 #if !HAVE_COMPATIBLE_ASIN
547     errno = 0;
548 #endif  // HAVE_COMPATIBLE_ASIN
549
550     ret = asinf(x);
551
552 #if !HAVE_COMPATIBLE_ASIN
553     if (errno == EDOM)
554     {
555         ret = PAL_NAN_FLT;  // NaN
556     }
557 #endif  // HAVE_COMPATIBLE_ASIN
558
559     LOGEXIT("asinf returns float %f\n", ret);
560     PERF_EXIT(asinf);
561     return ret;
562 }
563
564 /*++
565 Function:
566     atan2f
567
568 See MSDN.
569 --*/
570 PALIMPORT float __cdecl PAL_atan2f(float y, float x)
571 {
572     float ret;
573     PERF_ENTRY(atan2f);
574     ENTRY("atan2f (y=%f, x=%f)\n", y, x);
575
576 #if !HAVE_COMPATIBLE_ATAN2
577     errno = 0;
578 #endif  // !HAVE_COMPATIBLE_ATAN2
579
580     ret = atan2f(y, x);
581
582 #if !HAVE_COMPATIBLE_ATAN2
583     if ((errno == EDOM) && (x == 0.0f) && (y == 0.0f))
584     {
585         const float sign_x = copysign(1.0f, x);
586         const float sign_y = copysign(1.0f, y);
587
588         if (sign_x > 0)
589         {
590             ret = copysign(0.0f, sign_y);
591         }
592         else
593         {
594             ret = copysign(atan2f(0.0f, -1.0f), sign_y);
595         }
596     }
597 #endif  // !HAVE_COMPATIBLE_ATAN2
598
599     LOGEXIT("atan2f returns float %f\n", ret);
600     PERF_EXIT(atan2f);
601     return ret;
602 }
603
604 /*++
605 Function:
606     expf
607
608 See MSDN.
609 --*/
610 PALIMPORT float __cdecl PAL_expf(float x)
611 {
612     float ret;
613     PERF_ENTRY(expf);
614     ENTRY("expf (x=%f)\n", x);
615
616 #if !HAVE_COMPATIBLE_EXP
617     if (x == 1.0f) 
618     {
619         ret = M_E;
620     }
621     else
622     {
623 #endif  // HAVE_COMPATIBLE_EXP
624
625     ret = expf(x);
626     
627 #if !HAVE_COMPATIBLE_EXP
628     }
629 #endif // HAVE_COMPATIBLE_EXP
630
631     LOGEXIT("expf returns float %f\n", ret);
632     PERF_EXIT(expf);
633     return ret;
634 }
635
636 /*++
637 Function:
638     logf
639
640 See MSDN.
641 --*/
642 PALIMPORT float __cdecl PAL_logf(float x)
643 {
644     float ret;
645     PERF_ENTRY(logf);
646     ENTRY("logf (x=%f)\n", x);
647
648 #if !HAVE_COMPATIBLE_LOG
649     errno = 0;
650 #endif  // !HAVE_COMPATIBLE_LOG
651
652     ret = logf(x);
653
654 #if !HAVE_COMPATIBLE_LOG
655     if ((errno == EDOM) && (x < 0))
656     {
657         ret = PAL_NAN_FLT;    // NaN
658     }
659 #endif  // !HAVE_COMPATIBLE_LOG
660
661     LOGEXIT("logf returns float %f\n", ret);
662     PERF_EXIT(logf);
663     return ret;
664 }
665
666 /*++
667 Function:
668     log10f
669
670 See MSDN.
671 --*/
672 PALIMPORT float __cdecl PAL_log10f(float x)
673 {
674     float ret;
675     PERF_ENTRY(log10f);
676     ENTRY("log10f (x=%f)\n", x);
677
678 #if !HAVE_COMPATIBLE_LOG10
679     errno = 0;
680 #endif  // !HAVE_COMPATIBLE_LOG10
681
682     ret = log10f(x);
683     
684 #if !HAVE_COMPATIBLE_LOG10
685     if ((errno == EDOM) && (x < 0))
686     {
687         ret = PAL_NAN_FLT;    // NaN
688     }
689 #endif  // !HAVE_COMPATIBLE_LOG10
690
691     LOGEXIT("log10f returns float %f\n", ret);
692     PERF_EXIT(log10f);
693     return ret;
694 }
695
696 /*++
697 Function:
698     powf
699
700 See MSDN.
701 --*/
702 PALIMPORT float __cdecl PAL_powf(float x, float y)
703 {
704     float ret;
705     PERF_ENTRY(powf);
706     ENTRY("powf (x=%f, y=%f)\n", x, y);
707
708 #if !HAVE_COMPATIBLE_POW
709     if ((y == PAL_POSINF_FLT) && !isnan(x))    // +Inf
710     {
711         if (x == 1.0f)
712         {
713             ret = x;
714         }
715         else if (x == -1.0f)
716         {
717             ret = PAL_NAN_FLT;    // NaN
718         }
719         else if ((x > -1.0f) && (x < 1.0f))
720         {
721             ret = 0.0f;
722         }
723         else
724         {
725             ret = PAL_POSINF_FLT;    // +Inf
726         }
727     }
728     else if ((y == PAL_NEGINF_FLT) && !isnan(x))   // -Inf
729     {
730         if (x == 1.0f)
731         {
732             ret = x;
733         }
734         else if (x == -1.0f)
735         {
736             ret = PAL_NAN_FLT;    // NaN
737         }
738         else if ((x > -1.0f) && (x < 1.0f))
739         {
740             ret = PAL_POSINF_FLT;    // +Inf
741         }
742         else
743         {
744             ret = 0.0f;
745         }
746     }
747     else if (IS_FLT_NEGZERO(x) && (y == -1.0f))
748     {
749         ret = PAL_NEGINF_FLT;    // -Inf
750     }
751     else if ((x == 0.0f) && (y < 0.0f))
752     {
753         ret = PAL_POSINF_FLT;    // +Inf
754     }
755     else
756 #endif  // !HAVE_COMPATIBLE_POW
757
758     if ((y == 0.0f) && isnan(x))
759     {
760         // Windows returns NaN for powf(NaN, 0), but POSIX specifies
761         // a return value of 1 for that case.  We need to return
762         // the same result as Windows.
763         ret = PAL_NAN_FLT;
764     }
765     else
766     {
767         ret = powf(x, y);
768     }
769     
770 #if !HAVE_VALID_NEGATIVE_INF_POW
771     if ((ret == PAL_POSINF_FLT) && (x < 0) && isfinite(x) && (ceilf(y / 2) != floorf(y / 2)))
772     {
773         ret = PAL_NEGINF_FLT;   // -Inf
774     }
775 #endif  // !HAVE_VALID_NEGATIVE_INF_POW
776
777 #if !HAVE_VALID_POSITIVE_INF_POW
778     /*
779     * The (ceil(y/2) == floor(y/2)) test is slower, but more robust for platforms where large y
780     * will return the wrong result for ((long) y % 2 == 0). See PAL_pow(double) above for more details.
781     */
782     if ((ret == PAL_NEGINF_FLT) && (x < 0) && isfinite(x) && (ceilf(y / 2) == floorf(y / 2)))
783     {
784         ret = PAL_POSINF_FLT;   // +Inf
785     }
786 #endif  // !HAVE_VALID_POSITIVE_INF_POW
787
788     LOGEXIT("powf returns float %f\n", ret);
789     PERF_EXIT(powf);
790     return ret;
791 }