[linker] We need to mark nested types even if the declaring type isn't marked.
[mono-project.git] / mono / metadata / sysmath.c
blob8d55a552f84dbc86d35b01deea7793039e7b007d
1 /*
2 * sysmath.c: these are based on bob smith's csharp routines
4 * Author:
5 * Mono Project (http://www.mono-project.com)
6 * Ludovic Henry (ludovic@xamarin.com)
8 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
9 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
10 * Copyright 2015 Xamarin, Inc (https://www.xamarin.com)
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15 // Copyright (c) Microsoft. All rights reserved.
16 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
18 // Files:
19 // - src/classlibnative/float/floatnative.cpp
20 // - src/pal/src/cruntime/floatnative.cpp
22 // Ported from C++ to C and adjusted to Mono runtime
24 #define __USE_ISOC99
26 #include <math.h>
27 #include <mono/metadata/sysmath.h>
29 #include "number-ms.h"
30 #include "utils/mono-compiler.h"
32 static const MonoDouble_double NaN = { .s = { .sign = 0x0, .exp = 0x7FF, .mantHi = 0x80000, .mantLo = 0x0 } };
34 /* +Infinity */
35 static const MonoDouble_double PInfinity = { .s = { .sign = 0x0, .exp = 0x7FF, .mantHi = 0x0, .mantLo = 0x0 } };
37 /* -Infinity */
38 static const MonoDouble_double MInfinity = { .s = { .sign = 0x1, .exp = 0x7FF, .mantHi = 0x0, .mantLo = 0x0 } };
40 /* +1 */
41 static const MonoDouble_double POne = { .s = { .sign = 0x0, .exp = 0x3FF, .mantHi = 0x0, .mantLo = 0x0 } };
43 /* -1 */
44 static const MonoDouble_double MOne = { .s = { .sign = 0x1, .exp = 0x3FF, .mantHi = 0x0, .mantLo = 0x0 } };
46 static MONO_ALWAYS_INLINE gboolean
47 isplusinfinity (gdouble d)
49 return d == PInfinity.d;
52 static MONO_ALWAYS_INLINE gboolean
53 isminusinfinity (gdouble d)
55 return d == MInfinity.d;
58 static MONO_ALWAYS_INLINE gboolean
59 isinfinity (gdouble d)
61 return isplusinfinity (d) || isminusinfinity (d);
64 static MONO_ALWAYS_INLINE gboolean
65 isplusone (gdouble d)
67 return d == POne.d;
70 static MONO_ALWAYS_INLINE gboolean
71 isminusone (gdouble d)
73 return d == MOne.d;
76 gdouble
77 ves_icall_System_Math_Floor (gdouble x)
79 return floor(x);
82 gdouble
83 ves_icall_System_Math_Round (gdouble x)
85 gdouble tmp, floor_tmp;
87 /* If the number has no fractional part do nothing This shortcut is necessary
88 * to workaround precision loss in borderline cases on some platforms */
89 if (x == (gdouble)(gint64) x)
90 return x;
92 tmp = x + 0.5;
93 floor_tmp = floor (tmp);
95 if (floor_tmp == tmp) {
96 if (fmod (tmp, 2.0) != 0)
97 floor_tmp -= 1.0;
100 return copysign (floor_tmp, x);
103 gdouble
104 ves_icall_System_Math_Sin (gdouble x)
106 return sin (x);
109 gdouble
110 ves_icall_System_Math_Cos (gdouble x)
112 return cos (x);
115 gdouble
116 ves_icall_System_Math_Tan (gdouble x)
118 return tan (x);
121 gdouble
122 ves_icall_System_Math_Sinh (gdouble x)
124 return sinh (x);
127 gdouble
128 ves_icall_System_Math_Cosh (gdouble x)
130 return cosh (x);
133 gdouble
134 ves_icall_System_Math_Tanh (gdouble x)
136 return tanh (x);
139 gdouble
140 ves_icall_System_Math_Acos (gdouble x)
142 if (x < -1 || x > 1)
143 return NaN.d;
145 return acos (x);
148 gdouble
149 ves_icall_System_Math_Asin (gdouble x)
151 if (x < -1 || x > 1)
152 return NaN.d;
154 return asin (x);
157 gdouble
158 ves_icall_System_Math_Atan (gdouble x)
160 return atan (x);
163 gdouble
164 ves_icall_System_Math_Atan2 (gdouble y, gdouble x)
166 gdouble result;
168 if (isinfinity (x) && isinfinity (y))
169 return NaN.d;
171 result = atan2 (y, x);
172 return result == -0.0 ? 0.0: result;
175 gdouble
176 ves_icall_System_Math_Exp (gdouble x)
178 if (isinfinity (x))
179 return x < 0 ? 0.0 : x;
181 return exp (x);
184 gdouble
185 ves_icall_System_Math_Log (gdouble x)
187 if (x == 0)
188 return MInfinity.d;
189 else if (x < 0)
190 return NaN.d;
192 return log (x);
195 gdouble
196 ves_icall_System_Math_Log10 (gdouble x)
198 if (x == 0)
199 return MInfinity.d;
200 else if (x < 0)
201 return NaN.d;
203 return log10 (x);
206 gdouble
207 ves_icall_System_Math_Pow (gdouble x, gdouble y)
209 gdouble result;
211 if (isnan (y))
212 return y;
213 if (isnan (x))
214 return x;
216 if (isinfinity (y)) {
217 if (isplusone (x))
218 return x;
219 if (isminusone (x))
220 return NaN.d;
223 /* following are cases from PAL_pow which abstract the implementation of pow for posix and win32 platforms
224 * (https://github.com/dotnet/coreclr/blob/master/src/pal/src/cruntime/finite.cpp#L331) */
226 if (isplusinfinity (y) && !isnan (x)) {
227 if (isplusone (x) || isminusone (x))
228 result = NaN.d;
229 else if (x > MOne.d && x < POne.d)
230 result = 0.0;
231 else
232 result = PInfinity.d;
233 } else if (isminusinfinity (y) && !isnan (x)) {
234 if (isplusone (x) || isminusone (x))
235 result = NaN.d;
236 if (x > MOne.d && x < POne.d)
237 result = PInfinity.d;
238 else
239 result = 0.0;
240 } else if (x == 0.0 && y < 0.0) {
241 result = PInfinity.d;
242 } else if (y == 0.0 && isnan (x)) {
243 /* Windows returns NaN for pow(NaN, 0), but POSIX specifies
244 * a return value of 1 for that case. We need to return
245 * the same result as Windows. */
246 result = NaN.d;
247 } else {
248 result = pow (x, y);
251 if (result == PInfinity.d && x < 0.0 && isfinite (x) && ceil (y / 2) != floor (y / 2))
252 result = MInfinity.d;
255 * The even/odd test in the if (this one and the one above) used to be ((long long) y % 2 == 0)
256 * on SPARC (long long) y for large y (>2**63) is always 0x7fffffff7fffffff, which
257 * is an odd number, so the test ((long long) y % 2 == 0) will always fail for
258 * large y. Since large double numbers are always even (e.g., the representation of
259 * 1E20+1 is the same as that of 1E20, the last .+1. is too insignificant to be part
260 * of the representation), this test will always return the wrong result for large y.
262 * The (ceil(y/2) == floor(y/2)) test is slower, but more robust.
264 if (result == MInfinity.d && x < 0.0 && isfinite (x) && ceil (y / 2) == floor (y / 2))
265 result = PInfinity.d;
267 #if defined (__linux__) && SIZEOF_VOID_P == 4
268 /* On Linux 32bits, some tests erroneously return NaN */
269 if (isnan (result)) {
270 if (isminusone (x) && (y > 9007199254740991.0 || y < -9007199254740991.0)) {
271 /* Math.Pow (-1, Double.MaxValue) and Math.Pow (-1, Double.MinValue) should return 1 */
272 result = POne.d;
273 } else if (x < -9007199254740991.0 && y < -9007199254740991.0) {
274 /* Math.Pow (Double.MinValue, Double.MinValue) should return 0 */
275 result = 0.0;
276 } else if (x < -9007199254740991.0 && y > 9007199254740991.0) {
277 /* Math.Pow (Double.MinValue, Double.MaxValue) should return Double.PositiveInfinity */
278 result = PInfinity.d;
281 #endif
283 return result == -0.0 ? 0 : result;
286 gdouble
287 ves_icall_System_Math_Sqrt (gdouble x)
289 if (x < 0)
290 return NaN.d;
292 return sqrt (x);
295 gdouble
296 ves_icall_System_Math_Abs_double (gdouble v)
298 return fabs (v);
301 gfloat
302 ves_icall_System_Math_Abs_single (gfloat v)
304 return fabsf (v);
307 gdouble
308 ves_icall_System_Math_Ceiling (gdouble v)
310 return ceil (v);
313 gdouble
314 ves_icall_System_Math_SplitFractionDouble (gdouble *v)
316 return modf (*v, v);