Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / sysmath.c
1 /**
2  * \file
3  * these are based on bob smith's csharp routines 
4  *
5  * Author:
6  *      Mono Project (http://www.mono-project.com)
7  *      Ludovic Henry (ludovic@xamarin.com)
8  *
9  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
11  * Copyright 2015 Xamarin, Inc (https://www.xamarin.com)
12  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13  */
14
15 //
16 // Copyright (c) Microsoft. All rights reserved.
17 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
18 //
19 // Files:
20 //  - src/classlibnative/float/floatnative.cpp
21 //  - src/pal/src/cruntime/floatnative.cpp
22 //
23 // Ported from C++ to C and adjusted to Mono runtime
24
25 #define __USE_ISOC99
26
27 #include <math.h>
28 #include <mono/metadata/sysmath.h>
29
30 #include "number-ms.h"
31 #include "utils/mono-compiler.h"
32
33 static const MonoDouble_double NaN = { .s = { .sign = 0x0, .exp = 0x7FF, .mantHi = 0x80000, .mantLo = 0x0 } };
34
35 /* +Infinity */
36 static const MonoDouble_double PInfinity = { .s = { .sign = 0x0, .exp = 0x7FF, .mantHi = 0x0, .mantLo = 0x0 } };
37
38 /* -Infinity */
39 static const MonoDouble_double MInfinity = { .s = { .sign = 0x1, .exp = 0x7FF, .mantHi = 0x0, .mantLo = 0x0 } };
40
41 /* +1 */
42 static const MonoDouble_double POne = { .s = { .sign = 0x0, .exp = 0x3FF, .mantHi = 0x0, .mantLo = 0x0 } };
43
44 /* -1 */
45 static const MonoDouble_double MOne = { .s = { .sign = 0x1, .exp = 0x3FF, .mantHi = 0x0, .mantLo = 0x0 } };
46
47 static MONO_ALWAYS_INLINE gboolean
48 isplusinfinity (gdouble d)
49 {
50         return d == PInfinity.d;
51 }
52
53 static MONO_ALWAYS_INLINE gboolean
54 isminusinfinity (gdouble d)
55 {
56         return d == MInfinity.d;
57 }
58
59 static MONO_ALWAYS_INLINE gboolean
60 isinfinity (gdouble d)
61 {
62         return isplusinfinity (d) || isminusinfinity (d);
63 }
64
65 static MONO_ALWAYS_INLINE gboolean
66 isplusone (gdouble d)
67 {
68         return d == POne.d;
69 }
70
71 static MONO_ALWAYS_INLINE gboolean
72 isminusone (gdouble d)
73 {
74         return d == MOne.d;
75 }
76
77 gdouble
78 ves_icall_System_Math_Floor (gdouble x)
79 {
80         return floor(x);
81 }
82
83 gdouble
84 ves_icall_System_Math_Round (gdouble x)
85 {
86         gdouble tmp, floor_tmp;
87
88         /* If the number has no fractional part do nothing This shortcut is necessary
89          * to workaround precision loss in borderline cases on some platforms */
90         if (x == (gdouble)(gint64) x)
91                 return x;
92
93         tmp = x + 0.5;
94         floor_tmp = floor (tmp);
95
96         if (floor_tmp == tmp) {
97                 if (fmod (tmp, 2.0) != 0)
98                         floor_tmp -= 1.0;
99         }
100
101         return copysign (floor_tmp, x);
102 }
103
104 gdouble 
105 ves_icall_System_Math_Sin (gdouble x)
106 {
107         return sin (x);
108 }
109
110 gdouble 
111 ves_icall_System_Math_Cos (gdouble x)
112 {
113         return cos (x);
114 }
115
116 gdouble 
117 ves_icall_System_Math_Tan (gdouble x)
118 {
119         return tan (x);
120 }
121
122 gdouble 
123 ves_icall_System_Math_Sinh (gdouble x)
124 {
125         return sinh (x);
126 }
127
128 gdouble 
129 ves_icall_System_Math_Cosh (gdouble x)
130 {
131         return cosh (x);
132 }
133
134 gdouble 
135 ves_icall_System_Math_Tanh (gdouble x)
136 {
137         return tanh (x);
138 }
139
140 gdouble 
141 ves_icall_System_Math_Acos (gdouble x)
142 {
143         if (x < -1 || x > 1)
144                 return NaN.d;
145
146         return acos (x);
147 }
148
149 gdouble 
150 ves_icall_System_Math_Asin (gdouble x)
151 {
152         if (x < -1 || x > 1)
153                 return NaN.d;
154
155         return asin (x);
156 }
157
158 gdouble 
159 ves_icall_System_Math_Atan (gdouble x)
160 {
161         return atan (x);
162 }
163
164 gdouble 
165 ves_icall_System_Math_Atan2 (gdouble y, gdouble x)
166 {
167         gdouble result;
168
169         if (isinfinity (x) && isinfinity (y))
170                 return NaN.d;
171
172         result = atan2 (y, x);
173         return result == -0.0 ? 0.0: result;
174 }
175
176 gdouble 
177 ves_icall_System_Math_Exp (gdouble x)
178 {
179         if (isinfinity (x))
180                 return x < 0 ? 0.0 : x;
181
182         return exp (x);
183 }
184
185 gdouble 
186 ves_icall_System_Math_Log (gdouble x)
187 {
188         if (x == 0)
189                 return MInfinity.d;
190         else if (x < 0)
191                 return NaN.d;
192
193         return log (x);
194 }
195
196 gdouble 
197 ves_icall_System_Math_Log10 (gdouble x)
198 {
199         if (x == 0)
200                 return MInfinity.d;
201         else if (x < 0)
202                 return NaN.d;
203
204         return log10 (x);
205 }
206
207 gdouble 
208 ves_icall_System_Math_Pow (gdouble x, gdouble y)
209 {
210         gdouble result;
211
212         if (isnan (y))
213                 return y;
214         if (isnan (x))
215                 return x;
216
217         if (isinfinity (y)) {
218                 if (isplusone (x))
219                         return x;
220                 if (isminusone (x))
221                         return NaN.d;
222         }
223
224         /* following are cases from PAL_pow which abstract the implementation of pow for posix and win32 platforms
225          * (https://github.com/dotnet/coreclr/blob/master/src/pal/src/cruntime/finite.cpp#L331) */
226
227         if (isplusinfinity (y) && !isnan (x)) {
228                 if (isplusone (x) || isminusone (x))
229                         result = NaN.d;
230                 else if (x > MOne.d && x < POne.d)
231                         result = 0.0;
232                 else
233                         result = PInfinity.d;
234         } else if (isminusinfinity (y) && !isnan (x)) {
235                 if (isplusone (x) || isminusone (x))
236                         result = NaN.d;
237                 if (x > MOne.d && x < POne.d)
238                         result = PInfinity.d;
239                 else
240                         result = 0.0;
241         } else if (x == 0.0 && y < 0.0) {
242                 result = PInfinity.d;
243         } else if (y == 0.0 && isnan (x)) {
244                 /* Windows returns NaN for pow(NaN, 0), but POSIX specifies
245                  * a return value of 1 for that case.  We need to return
246                  * the same result as Windows. */
247                 result = NaN.d;
248         } else {
249                 result = pow (x, y);
250         }
251
252         if (result == PInfinity.d && x < 0.0 && isfinite (x) && ceil (y / 2) != floor (y / 2))
253                 result = MInfinity.d;
254
255         /*
256          * The even/odd test in the if (this one and the one above) used to be ((long long) y % 2 == 0)
257          * on SPARC (long long) y for large y (>2**63) is always 0x7fffffff7fffffff, which
258          * is an odd number, so the test ((long long) y % 2 == 0) will always fail for
259          * large y. Since large double numbers are always even (e.g., the representation of
260          * 1E20+1 is the same as that of 1E20, the last .+1. is too insignificant to be part
261          * of the representation), this test will always return the wrong result for large y.
262          *
263          * The (ceil(y/2) == floor(y/2)) test is slower, but more robust.
264          */
265         if (result == MInfinity.d && x < 0.0 && isfinite (x) && ceil (y / 2) == floor (y / 2))
266                 result = PInfinity.d;
267
268 #if defined (__linux__) && SIZEOF_VOID_P == 4
269         /* On Linux 32bits, some tests erroneously return NaN */
270         if (isnan (result)) {
271                 if (isminusone (x) && (y > 9007199254740991.0 || y < -9007199254740991.0)) {
272                         /* Math.Pow (-1, Double.MaxValue) and Math.Pow (-1, Double.MinValue) should return 1 */
273                         result = POne.d;
274                 } else if (x < -9007199254740991.0 && y < -9007199254740991.0) {
275                         /* Math.Pow (Double.MinValue, Double.MinValue) should return 0 */
276                         result = 0.0;
277                 } else if (x < -9007199254740991.0 && y > 9007199254740991.0) {
278                         /* Math.Pow (Double.MinValue, Double.MaxValue) should return Double.PositiveInfinity */
279                         result = PInfinity.d;
280                 }
281         }
282 #endif
283
284         return result == -0.0 ? 0 : result;
285 }
286
287 gdouble 
288 ves_icall_System_Math_Sqrt (gdouble x)
289 {
290         if (x < 0)
291                 return NaN.d;
292
293         return sqrt (x);
294 }
295
296 gdouble
297 ves_icall_System_Math_Abs_double (gdouble v)
298 {
299         return fabs (v);
300 }
301
302 gfloat
303 ves_icall_System_Math_Abs_single (gfloat v)
304 {
305         return fabsf (v);
306 }
307
308 gdouble
309 ves_icall_System_Math_Ceiling (gdouble v)
310 {
311         return ceil (v);
312 }
313
314 gdouble
315 ves_icall_System_Math_SplitFractionDouble (gdouble *v)
316 {
317         return modf (*v, v);
318 }