Merge pull request #3074 from Dmitry-Me/useElseIfForExclusive
[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 SET_DEFAULT_DEBUG_CHANNEL(CRT);
39
40 /*++
41 Function:
42   _finite
43
44 Determines whether given double-precision floating point value is finite.
45
46 Return Value
47
48 _finite returns a nonzero value (TRUE) if its argument x is not
49 infinite, that is, if -INF < x < +INF. It returns 0 (FALSE) if the
50 argument is infinite or a NaN.
51
52 Parameter
53
54 x  Double-precision floating-point value
55
56 --*/
57 int __cdecl _finite(double x)
58 {
59     int ret;
60     PERF_ENTRY(_finite);
61     ENTRY("_finite (x=%f)\n", x);
62
63 #if defined(_IA64_) && defined (_HPUX_)
64     ret = !isnan(x) && (x != PAL_POSINF_DBL) && (x != PAL_NEGINF_DBL);
65 #else
66     ret = isfinite(x);
67 #endif
68
69     LOGEXIT("_finite returns int %d\n", ret);
70     PERF_EXIT(_finite);
71     return ret;
72 }
73
74 /*++
75 Function:
76   _isnan
77
78 See MSDN doc
79 --*/
80 int __cdecl _isnan(double x)
81 {
82     int ret;
83     PERF_ENTRY(_isnan);
84     ENTRY("_isnan (x=%f)\n", x);
85
86     ret = isnan(x);
87
88     LOGEXIT("_isnan returns int %d\n", ret);
89     PERF_EXIT(_isnan);
90     return ret;
91 }
92
93 /*++
94 Function:
95   _copysign
96
97 See MSDN doc
98 --*/
99 double __cdecl _copysign(double x, double y)
100 {
101     double ret;
102     PERF_ENTRY(_copysign);
103     ENTRY("_copysign (x=%f, y=%f)\n", x, y);
104
105     ret = copysign(x, y);
106
107     LOGEXIT("_copysign returns double %f\n", ret);
108     PERF_EXIT(_copysign);
109     return ret;
110 }
111
112 /*++
113 Function:
114     acos
115
116 See MSDN.
117 --*/
118 PALIMPORT double __cdecl PAL_acos(double x)
119 {
120     double ret;
121     PERF_ENTRY(acos);
122     ENTRY("acos (x=%f)\n", x);
123
124 #if !HAVE_COMPATIBLE_ACOS
125     errno = 0;
126 #endif  // HAVE_COMPATIBLE_ACOS
127
128     ret = acos(x);
129     
130 #if !HAVE_COMPATIBLE_ACOS
131     if (errno == EDOM)
132     {
133         ret = PAL_NAN_DBL;  // NaN
134     }
135 #endif  // HAVE_COMPATIBLE_ACOS
136
137     LOGEXIT("acos returns double %f\n", ret);
138     PERF_EXIT(acos);
139     return ret;
140 }
141
142 /*++
143 Function:
144     asin
145
146 See MSDN.
147 --*/
148 PALIMPORT double __cdecl PAL_asin(double x)
149 {
150     double ret;
151     PERF_ENTRY(asin);
152     ENTRY("asin (x=%f)\n", x);
153
154 #if !HAVE_COMPATIBLE_ASIN
155     errno = 0;
156 #endif  // HAVE_COMPATIBLE_ASIN
157
158     ret = asin(x);
159
160 #if !HAVE_COMPATIBLE_ASIN
161     if (errno == EDOM)
162     {
163         ret = PAL_NAN_DBL;  // NaN
164     }
165 #endif  // HAVE_COMPATIBLE_ASIN
166
167     LOGEXIT("asin returns double %f\n", ret);
168     PERF_EXIT(asin);
169     return ret;
170 }
171
172 /*++
173 Function:
174     atan2
175
176 See MSDN.
177 --*/
178 PALIMPORT double __cdecl PAL_atan2(double y, double x)
179 {
180     double ret;
181     PERF_ENTRY(atan2);
182     ENTRY("atan2 (y=%f, x=%f)\n", y, x);
183
184 #if !HAVE_COMPATIBLE_ATAN2
185     errno = 0;
186 #endif  // !HAVE_COMPATIBLE_ATAN2
187
188     ret = atan2(y, x);
189
190 #if !HAVE_COMPATIBLE_ATAN2
191     if ((errno == EDOM) && (x == 0.0) && (y == 0.0))
192     {
193         const double sign_x = copysign(1.0, x);
194         const double sign_y = copysign(1.0, y);
195
196         if (sign_x > 0)
197         {
198             ret = copysign(0.0, sign_y);
199         }
200         else
201         {
202             ret = copysign(atan2(0.0, -1.0), sign_y);
203         }
204     }
205 #endif  // !HAVE_COMPATIBLE_ATAN2
206
207     LOGEXIT("atan2 returns double %f\n", ret);
208     PERF_EXIT(atan2);
209     return ret;
210 }
211
212 /*++
213 Function:
214     exp
215
216 See MSDN.
217 --*/
218 PALIMPORT double __cdecl PAL_exp(double x)
219 {
220     double ret;
221     PERF_ENTRY(exp);
222     ENTRY("exp (x=%f)\n", x);
223
224 #if !HAVE_COMPATIBLE_EXP
225     if (x == 1.0) 
226     {
227         ret = M_E;
228     }
229     else
230     {
231 #endif  // HAVE_COMPATIBLE_EXP
232
233     ret = exp(x);
234     
235 #if !HAVE_COMPATIBLE_EXP
236     }
237 #endif // HAVE_COMPATIBLE_EXP
238
239     LOGEXIT("exp returns double %f\n", ret);
240     PERF_EXIT(exp);
241     return ret;
242 }
243
244 /*++
245 Function:
246     labs
247
248 See MSDN.
249 --*/
250 PALIMPORT LONG __cdecl PAL_labs(LONG l)
251 {
252     long lRet;
253     PERF_ENTRY(labs);
254     ENTRY("labs (l=%ld)\n", l);
255     
256     lRet = labs(l);    
257
258     LOGEXIT("labs returns long %ld\n", lRet);
259     PERF_EXIT(labs);
260     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.
261 }
262
263 /*++
264 Function:
265     log
266
267 See MSDN.
268 --*/
269 PALIMPORT double __cdecl PAL_log(double x)
270 {
271     double ret;
272     PERF_ENTRY(log);
273     ENTRY("log (x=%f)\n", x);
274
275 #if !HAVE_COMPATIBLE_LOG
276     errno = 0;
277 #endif  // !HAVE_COMPATIBLE_LOG
278
279     ret = log(x);
280
281 #if !HAVE_COMPATIBLE_LOG
282     if ((errno == EDOM) && (x < 0))
283     {
284         ret = PAL_NAN_DBL;    // NaN
285     }
286 #endif  // !HAVE_COMPATIBLE_LOG
287
288     LOGEXIT("log returns double %f\n", ret);
289     PERF_EXIT(log);
290     return ret;
291 }
292
293 /*++
294 Function:
295     log10
296
297 See MSDN.
298 --*/
299 PALIMPORT double __cdecl PAL_log10(double x)
300 {
301     double ret;
302     PERF_ENTRY(log10);
303     ENTRY("log10 (x=%f)\n", x);
304
305 #if !HAVE_COMPATIBLE_LOG10
306     errno = 0;
307 #endif  // !HAVE_COMPATIBLE_LOG10
308
309     ret = log10(x);
310     
311 #if !HAVE_COMPATIBLE_LOG10
312     if ((errno == EDOM) && (x < 0))
313     {
314         ret = PAL_NAN_DBL;    // NaN
315     }
316 #endif  // !HAVE_COMPATIBLE_LOG10
317
318     LOGEXIT("log10 returns double %f\n", ret);
319     PERF_EXIT(log10);
320     return ret;
321 }
322
323 /*++
324 Function:
325     pow
326
327 See MSDN.
328 --*/
329 PALIMPORT double __cdecl PAL_pow(double x, double y)
330 {
331     double ret;
332     PERF_ENTRY(pow);
333     ENTRY("pow (x=%f, y=%f)\n", x, y);
334
335 #if !HAVE_COMPATIBLE_POW
336     if ((y == PAL_POSINF_DBL) && !isnan(x))    // +Inf
337     {
338         if (x == 1.0)
339         {
340             ret = x;
341         }
342         else if (x == -1.0)
343         {
344             ret = PAL_NAN_DBL;    // NaN
345         }
346         else if ((x > -1.0) && (x < 1.0))
347         {
348             ret = 0.0;
349         }
350         else
351         {
352             ret = PAL_POSINF_DBL;    // +Inf
353         }
354     }
355     else if ((y == PAL_NEGINF_DBL) && !isnan(x))   // -Inf
356     {
357         if (x == 1.0)
358         {
359             ret = x;
360         }
361         else if (x == -1.0)
362         {
363             ret = PAL_NAN_DBL;    // NaN
364         }
365         else if ((x > -1.0) && (x < 1.0))
366         {
367             ret = PAL_POSINF_DBL;    // +Inf
368         }
369         else
370         {
371             ret = 0.0;
372         }
373     }
374     else if (IS_DBL_NEGZERO(x) && (y == -1.0))
375     {
376         ret = PAL_NEGINF_DBL;    // -Inf
377     }
378     else if ((x == 0.0) && (y < 0.0))
379     {
380         ret = PAL_POSINF_DBL;    // +Inf
381     }
382     else
383 #endif  // !HAVE_COMPATIBLE_POW
384
385     if ((y == 0.0) && isnan(x))
386     {
387         // Windows returns NaN for pow(NaN, 0), but POSIX specifies
388         // a return value of 1 for that case.  We need to return
389         // the same result as Windows.
390         ret = PAL_NAN_DBL;
391     }
392     else
393     {
394         ret = pow(x, y);
395     }
396
397 #if !HAVE_VALID_NEGATIVE_INF_POW
398     if ((ret == PAL_POSINF_DBL) && (x < 0) && isfinite(x) && (ceil(y / 2) != floor(y / 2)))
399     {
400         ret = PAL_NEGINF_DBL;   // -Inf
401     }
402 #endif  // !HAVE_VALID_NEGATIVE_INF_POW
403
404 #if !HAVE_VALID_POSITIVE_INF_POW
405     /*
406     * The even/odd test in the if (this one and the one above) used to be ((long long) y % 2 == 0)
407     * on SPARC (long long) y for large y (>2**63) is always 0x7fffffff7fffffff, which
408     * is an odd number, so the test ((long long) y % 2 == 0) will always fail for
409     * large y. Since large double numbers are always even (e.g., the representation of
410     * 1E20+1 is the same as that of 1E20, the last .+1. is too insignificant to be part
411     * of the representation), this test will always return the wrong result for large y.
412     * 
413     * The (ceil(y/2) == floor(y/2)) test is slower, but more robust.
414     */
415     if ((ret == PAL_NEGINF_DBL) && (x < 0) && isfinite(x) && (ceil(y / 2) == floor(y / 2)))
416     {
417         ret = PAL_POSINF_DBL;   // +Inf
418     }
419 #endif  // !HAVE_VALID_POSITIVE_INF_POW
420
421     LOGEXIT("pow returns double %f\n", ret);
422     PERF_EXIT(pow);
423     return ret;
424 }