2 * sysmath.c: these are based on bob smith's csharp routines
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.
19 // - src/classlibnative/float/floatnative.cpp
20 // - src/pal/src/cruntime/floatnative.cpp
22 // Ported from C++ to C and adjusted to Mono runtime
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 } };
35 static const MonoDouble_double PInfinity
= { .s
= { .sign
= 0x0, .exp
= 0x7FF, .mantHi
= 0x0, .mantLo
= 0x0 } };
38 static const MonoDouble_double MInfinity
= { .s
= { .sign
= 0x1, .exp
= 0x7FF, .mantHi
= 0x0, .mantLo
= 0x0 } };
41 static const MonoDouble_double POne
= { .s
= { .sign
= 0x0, .exp
= 0x3FF, .mantHi
= 0x0, .mantLo
= 0x0 } };
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
70 static MONO_ALWAYS_INLINE gboolean
71 isminusone (gdouble d
)
77 ves_icall_System_Math_Floor (gdouble x
)
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
)
93 floor_tmp
= floor (tmp
);
95 if (floor_tmp
== tmp
) {
96 if (fmod (tmp
, 2.0) != 0)
100 return copysign (floor_tmp
, x
);
104 ves_icall_System_Math_Sin (gdouble x
)
110 ves_icall_System_Math_Cos (gdouble x
)
116 ves_icall_System_Math_Tan (gdouble x
)
122 ves_icall_System_Math_Sinh (gdouble x
)
128 ves_icall_System_Math_Cosh (gdouble x
)
134 ves_icall_System_Math_Tanh (gdouble x
)
140 ves_icall_System_Math_Acos (gdouble x
)
149 ves_icall_System_Math_Asin (gdouble x
)
158 ves_icall_System_Math_Atan (gdouble x
)
164 ves_icall_System_Math_Atan2 (gdouble y
, gdouble x
)
168 if (isinfinity (x
) && isinfinity (y
))
171 result
= atan2 (y
, x
);
172 return result
== -0.0 ? 0.0: result
;
176 ves_icall_System_Math_Exp (gdouble x
)
179 return x
< 0 ? 0.0 : x
;
185 ves_icall_System_Math_Log (gdouble x
)
196 ves_icall_System_Math_Log10 (gdouble x
)
207 ves_icall_System_Math_Pow (gdouble x
, gdouble y
)
216 if (isinfinity (y
)) {
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
))
229 else if (x
> MOne
.d
&& x
< POne
.d
)
232 result
= PInfinity
.d
;
233 } else if (isminusinfinity (y
) && !isnan (x
)) {
234 if (isplusone (x
) || isminusone (x
))
236 if (x
> MOne
.d
&& x
< POne
.d
)
237 result
= PInfinity
.d
;
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. */
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 */
273 } else if (x
< -9007199254740991.0 && y
< -9007199254740991.0) {
274 /* Math.Pow (Double.MinValue, Double.MinValue) should return 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
;
283 return result
== -0.0 ? 0 : result
;
287 ves_icall_System_Math_Sqrt (gdouble x
)
296 ves_icall_System_Math_Abs_double (gdouble v
)
302 ves_icall_System_Math_Abs_single (gfloat v
)
308 ves_icall_System_Math_Ceiling (gdouble v
)
314 ves_icall_System_Math_SplitFractionDouble (gdouble
*v
)