2008-12-08 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mono / metadata / sysmath.c
1 /* math.c - these are based on bob smith's csharp routines */
2
3 #define __USE_ISOC99
4 #include <math.h>
5 #include <mono/metadata/sysmath.h>
6 #include <mono/metadata/exception.h>
7
8 #ifndef NAN
9 # if G_BYTE_ORDER == G_BIG_ENDIAN
10 #  define __nan_bytes           { 0x7f, 0xc0, 0, 0 }
11 # endif
12 # if G_BYTE_ORDER == G_LITTLE_ENDIAN
13 #  define __nan_bytes           { 0, 0, 0xc0, 0x7f }
14 # endif
15
16 static union { unsigned char __c[4]; float __d; } __nan_union = { __nan_bytes };
17 # define NAN    (__nan_union.__d)
18 #endif
19
20 #ifndef HUGE_VAL
21 #define __huge_val_t   union { unsigned char __c[8]; double __d; }
22 # if G_BYTE_ORDER == G_BIG_ENDIAN
23 #  define __HUGE_VAL_bytes       { 0x7f, 0xf0, 0, 0, 0, 0, 0, 0 }
24 # endif
25 # if G_BYTE_ORDER == G_LITTLE_ENDIAN
26 #  define __HUGE_VAL_bytes       { 0, 0, 0, 0, 0, 0, 0xf0, 0x7f }
27 # endif
28 static __huge_val_t __huge_val = { __HUGE_VAL_bytes };
29 #  define HUGE_VAL      (__huge_val.__d)
30 #endif
31
32
33 gdouble ves_icall_System_Math_Floor (gdouble x) {
34         MONO_ARCH_SAVE_REGS;
35         return floor(x);
36 }
37
38 gdouble ves_icall_System_Math_Round (gdouble x) {
39         double int_part, dec_part;
40         MONO_ARCH_SAVE_REGS;
41         int_part = floor(x);
42         dec_part = x - int_part;
43         if (((dec_part == 0.5) &&
44                 ((2.0 * ((int_part / 2.0) - floor(int_part / 2.0))) != 0.0)) ||
45                 (dec_part > 0.5)) {
46                 int_part++;
47         }
48         return int_part;
49 }
50
51 gdouble ves_icall_System_Math_Round2 (gdouble value, gint32 digits, gboolean away_from_zero) {
52 #if !defined (HAVE_ROUND) || !defined (HAVE_RINT)
53         double int_part, dec_part;
54 #endif
55         double p;
56
57         MONO_ARCH_SAVE_REGS;
58         if (value == HUGE_VAL)
59                 return HUGE_VAL;
60         if (value == -HUGE_VAL)
61                 return -HUGE_VAL;
62         if (digits == 0)
63                 return ves_icall_System_Math_Round(value);
64         p = pow(10, digits);
65 #if defined (HAVE_ROUND) && defined (HAVE_RINT)
66         if (away_from_zero)
67                 return round (value * p) / p;
68         else
69                 return rint (value * p) / p;
70 #else
71         dec_part = modf (value, &int_part);
72         dec_part *= 1000000000000000ULL;
73         if (away_from_zero && dec_part > 0)
74                 dec_part = ceil (dec_part);
75         else
76                 dec_part = floor (dec_part);
77         dec_part /= (1000000000000000ULL / p);
78         if (away_from_zero) {
79                 if (dec_part > 0)
80                         dec_part = floor (dec_part + 0.5);
81                 else
82                         dec_part = ceil (dec_part - 0.5);
83         } else
84                 dec_part = ves_icall_System_Math_Round (dec_part);
85         dec_part /= p;
86         return ves_icall_System_Math_Round ((int_part + dec_part) * p) / p;
87 #endif
88 }
89
90 gdouble 
91 ves_icall_System_Math_Sin (gdouble x)
92 {
93         MONO_ARCH_SAVE_REGS;
94
95         return sin (x);
96 }
97
98 gdouble 
99 ves_icall_System_Math_Cos (gdouble x)
100 {
101         MONO_ARCH_SAVE_REGS;
102
103         return cos (x);
104 }
105
106 gdouble 
107 ves_icall_System_Math_Tan (gdouble x)
108 {
109         MONO_ARCH_SAVE_REGS;
110
111         return tan (x);
112 }
113
114 gdouble 
115 ves_icall_System_Math_Sinh (gdouble x)
116 {
117         MONO_ARCH_SAVE_REGS;
118
119         return sinh (x);
120 }
121
122 gdouble 
123 ves_icall_System_Math_Cosh (gdouble x)
124 {
125         MONO_ARCH_SAVE_REGS;
126
127         return cosh (x);
128 }
129
130 gdouble 
131 ves_icall_System_Math_Tanh (gdouble x)
132 {
133         MONO_ARCH_SAVE_REGS;
134
135         return tanh (x);
136 }
137
138 gdouble 
139 ves_icall_System_Math_Acos (gdouble x)
140 {
141         MONO_ARCH_SAVE_REGS;
142
143         if (x < -1 || x > 1)
144                 return NAN;
145
146         return acos (x);
147 }
148
149 gdouble 
150 ves_icall_System_Math_Asin (gdouble x)
151 {
152         MONO_ARCH_SAVE_REGS;
153
154         if (x < -1 || x > 1)
155                 return NAN;
156
157         return asin (x);
158 }
159
160 gdouble 
161 ves_icall_System_Math_Atan (gdouble x)
162 {
163         MONO_ARCH_SAVE_REGS;
164
165         return atan (x);
166 }
167
168 gdouble 
169 ves_icall_System_Math_Atan2 (gdouble y, gdouble x)
170 {
171         double result;
172         MONO_ARCH_SAVE_REGS;
173
174         if ((y == HUGE_VAL && x == HUGE_VAL) ||
175                 (y == HUGE_VAL && x == -HUGE_VAL) ||
176                 (y == -HUGE_VAL && x == HUGE_VAL) ||
177                 (y == -HUGE_VAL && x == -HUGE_VAL)) {
178                 return NAN;
179         }
180         result = atan2 (y, x);
181         return (result == -0)? 0: result;
182 }
183
184 gdouble 
185 ves_icall_System_Math_Exp (gdouble x)
186 {
187         MONO_ARCH_SAVE_REGS;
188
189         return exp (x);
190 }
191
192 gdouble 
193 ves_icall_System_Math_Log (gdouble x)
194 {
195         MONO_ARCH_SAVE_REGS;
196
197         if (x == 0)
198                 return -HUGE_VAL;
199         else if (x < 0)
200                 return NAN;
201
202         return log (x);
203 }
204
205 gdouble 
206 ves_icall_System_Math_Log10 (gdouble x)
207 {
208         MONO_ARCH_SAVE_REGS;
209
210         if (x == 0)
211                 return -HUGE_VAL;
212         else if (x < 0)
213                 return NAN;
214
215         return log10 (x);
216 }
217
218 gdouble 
219 ves_icall_System_Math_Pow (gdouble x, gdouble y)
220 {
221         double result;
222         MONO_ARCH_SAVE_REGS;
223
224         if (isnan(x) || isnan(y)) {
225                 return NAN;
226         }
227
228         if ((x == 1 || x == -1) && (y == HUGE_VAL || y == -HUGE_VAL)) {
229                 return NAN;
230         }
231
232         /* This code is for return the same results as MS.NET for certain
233          * limit values */
234         if (x < -9007199254740991.0) {
235                 if (y > 9007199254740991.0)
236                         return HUGE_VAL;
237                 if (y < -9007199254740991.0)
238                         return 0;
239         }
240
241         result = pow (x, y);
242
243         /* This code is for return the same results as MS.NET for certain
244          * limit values */
245         if (isnan(result) &&
246                 (x == -1.0) &&
247                 ((y > 9007199254740991.0) || (y < -9007199254740991.0))) {
248                 return 1;
249         }
250
251         return (result == -0)? 0: result;
252 }
253
254 gdouble 
255 ves_icall_System_Math_Sqrt (gdouble x)
256 {
257         MONO_ARCH_SAVE_REGS;
258
259         if (x < 0)
260                 return NAN;
261
262         return sqrt (x);
263 }