]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * Math built-ins | |
3 | */ | |
4 | ||
5 | #include "duk_internal.h" | |
6 | ||
7 | #if defined(DUK_USE_MATH_BUILTIN) | |
8 | ||
9 | /* | |
10 | * Use static helpers which can work with math.h functions matching | |
11 | * the following signatures. This is not portable if any of these math | |
12 | * functions is actually a macro. | |
13 | * | |
14 | * Typing here is intentionally 'double' wherever values interact with | |
15 | * the standard library APIs. | |
16 | */ | |
17 | ||
18 | typedef double (*duk__one_arg_func)(double); | |
19 | typedef double (*duk__two_arg_func)(double, double); | |
20 | ||
21 | DUK_LOCAL duk_ret_t duk__math_minmax(duk_context *ctx, duk_double_t initial, duk__two_arg_func min_max) { | |
22 | duk_idx_t n = duk_get_top(ctx); | |
23 | duk_idx_t i; | |
24 | duk_double_t res = initial; | |
25 | duk_double_t t; | |
26 | ||
27 | /* | |
28 | * Note: fmax() does not match the E5 semantics. E5 requires | |
29 | * that if -any- input to Math.max() is a NaN, the result is a | |
30 | * NaN. fmax() will return a NaN only if -both- inputs are NaN. | |
31 | * Same applies to fmin(). | |
32 | * | |
33 | * Note: every input value must be coerced with ToNumber(), even | |
34 | * if we know the result will be a NaN anyway: ToNumber() may have | |
35 | * side effects for which even order of evaluation matters. | |
36 | */ | |
37 | ||
38 | for (i = 0; i < n; i++) { | |
39 | t = duk_to_number(ctx, i); | |
40 | if (DUK_FPCLASSIFY(t) == DUK_FP_NAN || DUK_FPCLASSIFY(res) == DUK_FP_NAN) { | |
41 | /* Note: not normalized, but duk_push_number() will normalize */ | |
42 | res = (duk_double_t) DUK_DOUBLE_NAN; | |
43 | } else { | |
44 | res = (duk_double_t) min_max(res, (double) t); | |
45 | } | |
46 | } | |
47 | ||
48 | duk_push_number(ctx, res); | |
49 | return 1; | |
50 | } | |
51 | ||
52 | DUK_LOCAL double duk__fmin_fixed(double x, double y) { | |
53 | /* fmin() with args -0 and +0 is not guaranteed to return | |
54 | * -0 as Ecmascript requires. | |
55 | */ | |
56 | if (x == 0 && y == 0) { | |
57 | /* XXX: what's the safest way of creating a negative zero? */ | |
58 | if (DUK_SIGNBIT(x) != 0 || DUK_SIGNBIT(y) != 0) { | |
59 | return -0.0; | |
60 | } else { | |
61 | return +0.0; | |
62 | } | |
63 | } | |
64 | #ifdef DUK_USE_MATH_FMIN | |
65 | return DUK_FMIN(x, y); | |
66 | #else | |
67 | return (x < y ? x : y); | |
68 | #endif | |
69 | } | |
70 | ||
71 | DUK_LOCAL double duk__fmax_fixed(double x, double y) { | |
72 | /* fmax() with args -0 and +0 is not guaranteed to return | |
73 | * +0 as Ecmascript requires. | |
74 | */ | |
75 | if (x == 0 && y == 0) { | |
76 | if (DUK_SIGNBIT(x) == 0 || DUK_SIGNBIT(y) == 0) { | |
77 | return +0.0; | |
78 | } else { | |
79 | return -0.0; | |
80 | } | |
81 | } | |
82 | #ifdef DUK_USE_MATH_FMAX | |
83 | return DUK_FMAX(x, y); | |
84 | #else | |
85 | return (x > y ? x : y); | |
86 | #endif | |
87 | } | |
88 | ||
89 | DUK_LOCAL double duk__round_fixed(double x) { | |
90 | /* Numbers half-way between integers must be rounded towards +Infinity, | |
91 | * e.g. -3.5 must be rounded to -3 (not -4). When rounded to zero, zero | |
92 | * sign must be set appropriately. E5.1 Section 15.8.2.15. | |
93 | * | |
94 | * Note that ANSI C round() is "round to nearest integer, away from zero", | |
95 | * which is incorrect for negative values. Here we make do with floor(). | |
96 | */ | |
97 | ||
98 | duk_small_int_t c = (duk_small_int_t) DUK_FPCLASSIFY(x); | |
99 | if (c == DUK_FP_NAN || c == DUK_FP_INFINITE || c == DUK_FP_ZERO) { | |
100 | return x; | |
101 | } | |
102 | ||
103 | /* | |
104 | * x is finite and non-zero | |
105 | * | |
106 | * -1.6 -> floor(-1.1) -> -2 | |
107 | * -1.5 -> floor(-1.0) -> -1 (towards +Inf) | |
108 | * -1.4 -> floor(-0.9) -> -1 | |
109 | * -0.5 -> -0.0 (special case) | |
110 | * -0.1 -> -0.0 (special case) | |
111 | * +0.1 -> +0.0 (special case) | |
112 | * +0.5 -> floor(+1.0) -> 1 (towards +Inf) | |
113 | * +1.4 -> floor(+1.9) -> 1 | |
114 | * +1.5 -> floor(+2.0) -> 2 (towards +Inf) | |
115 | * +1.6 -> floor(+2.1) -> 2 | |
116 | */ | |
117 | ||
118 | if (x >= -0.5 && x < 0.5) { | |
119 | /* +0.5 is handled by floor, this is on purpose */ | |
120 | if (x < 0.0) { | |
121 | return -0.0; | |
122 | } else { | |
123 | return +0.0; | |
124 | } | |
125 | } | |
126 | ||
127 | return DUK_FLOOR(x + 0.5); | |
128 | } | |
129 | ||
130 | DUK_LOCAL double duk__pow_fixed(double x, double y) { | |
131 | /* The ANSI C pow() semantics differ from Ecmascript. | |
132 | * | |
133 | * E.g. when x==1 and y is +/- infinite, the Ecmascript required | |
134 | * result is NaN, while at least Linux pow() returns 1. | |
135 | */ | |
136 | ||
137 | duk_small_int_t cx, cy, sx; | |
138 | ||
139 | DUK_UNREF(cx); | |
140 | DUK_UNREF(sx); | |
141 | cy = (duk_small_int_t) DUK_FPCLASSIFY(y); | |
142 | ||
143 | if (cy == DUK_FP_NAN) { | |
144 | goto ret_nan; | |
145 | } | |
146 | if (DUK_FABS(x) == 1.0 && cy == DUK_FP_INFINITE) { | |
147 | goto ret_nan; | |
148 | } | |
149 | #if defined(DUK_USE_POW_NETBSD_WORKAROUND) | |
150 | /* See test-bug-netbsd-math-pow.js: NetBSD 6.0 on x86 (at least) does not | |
151 | * correctly handle some cases where x=+/-0. Specific fixes to these | |
152 | * here. | |
153 | */ | |
154 | cx = (duk_small_int_t) DUK_FPCLASSIFY(x); | |
155 | if (cx == DUK_FP_ZERO && y < 0.0) { | |
156 | sx = (duk_small_int_t) DUK_SIGNBIT(x); | |
157 | if (sx == 0) { | |
158 | /* Math.pow(+0,y) should be Infinity when y<0. NetBSD pow() | |
159 | * returns -Infinity instead when y is <0 and finite. The | |
160 | * if-clause also catches y == -Infinity (which works even | |
161 | * without the fix). | |
162 | */ | |
163 | return DUK_DOUBLE_INFINITY; | |
164 | } else { | |
165 | /* Math.pow(-0,y) where y<0 should be: | |
166 | * - -Infinity if y<0 and an odd integer | |
167 | * - Infinity otherwise | |
168 | * NetBSD pow() returns -Infinity for all finite y<0. The | |
169 | * if-clause also catches y == -Infinity (which works even | |
170 | * without the fix). | |
171 | */ | |
172 | ||
173 | /* fmod() return value has same sign as input (negative) so | |
174 | * the result here will be in the range ]-2,0], 1 indicates | |
175 | * odd. If x is -Infinity, NaN is returned and the odd check | |
176 | * always concludes "not odd" which results in desired outcome. | |
177 | */ | |
178 | double tmp = DUK_FMOD(y, 2); | |
179 | if (tmp == -1.0) { | |
180 | return -DUK_DOUBLE_INFINITY; | |
181 | } else { | |
182 | /* Not odd, or y == -Infinity */ | |
183 | return DUK_DOUBLE_INFINITY; | |
184 | } | |
185 | } | |
186 | } | |
187 | #endif | |
188 | return DUK_POW(x, y); | |
189 | ||
190 | ret_nan: | |
191 | return DUK_DOUBLE_NAN; | |
192 | } | |
193 | ||
194 | /* Wrappers for calling standard math library methods. These may be required | |
195 | * on platforms where one or more of the math built-ins are defined as macros | |
196 | * or inline functions and are thus not suitable to be used as function pointers. | |
197 | */ | |
198 | #if defined(DUK_USE_AVOID_PLATFORM_FUNCPTRS) | |
199 | DUK_LOCAL double duk__fabs(double x) { | |
200 | return DUK_FABS(x); | |
201 | } | |
202 | DUK_LOCAL double duk__acos(double x) { | |
203 | return DUK_ACOS(x); | |
204 | } | |
205 | DUK_LOCAL double duk__asin(double x) { | |
206 | return DUK_ASIN(x); | |
207 | } | |
208 | DUK_LOCAL double duk__atan(double x) { | |
209 | return DUK_ATAN(x); | |
210 | } | |
211 | DUK_LOCAL double duk__ceil(double x) { | |
212 | return DUK_CEIL(x); | |
213 | } | |
214 | DUK_LOCAL double duk__cos(double x) { | |
215 | return DUK_COS(x); | |
216 | } | |
217 | DUK_LOCAL double duk__exp(double x) { | |
218 | return DUK_EXP(x); | |
219 | } | |
220 | DUK_LOCAL double duk__floor(double x) { | |
221 | return DUK_FLOOR(x); | |
222 | } | |
223 | DUK_LOCAL double duk__log(double x) { | |
224 | return DUK_LOG(x); | |
225 | } | |
226 | DUK_LOCAL double duk__sin(double x) { | |
227 | return DUK_SIN(x); | |
228 | } | |
229 | DUK_LOCAL double duk__sqrt(double x) { | |
230 | return DUK_SQRT(x); | |
231 | } | |
232 | DUK_LOCAL double duk__tan(double x) { | |
233 | return DUK_TAN(x); | |
234 | } | |
235 | DUK_LOCAL double duk__atan2(double x, double y) { | |
236 | return DUK_ATAN2(x, y); | |
237 | } | |
238 | #endif /* DUK_USE_AVOID_PLATFORM_FUNCPTRS */ | |
239 | ||
240 | /* order must match constants in genbuiltins.py */ | |
241 | DUK_LOCAL const duk__one_arg_func duk__one_arg_funcs[] = { | |
242 | #if defined(DUK_USE_AVOID_PLATFORM_FUNCPTRS) | |
243 | duk__fabs, | |
244 | duk__acos, | |
245 | duk__asin, | |
246 | duk__atan, | |
247 | duk__ceil, | |
248 | duk__cos, | |
249 | duk__exp, | |
250 | duk__floor, | |
251 | duk__log, | |
252 | duk__round_fixed, | |
253 | duk__sin, | |
254 | duk__sqrt, | |
255 | duk__tan | |
256 | #else | |
257 | DUK_FABS, | |
258 | DUK_ACOS, | |
259 | DUK_ASIN, | |
260 | DUK_ATAN, | |
261 | DUK_CEIL, | |
262 | DUK_COS, | |
263 | DUK_EXP, | |
264 | DUK_FLOOR, | |
265 | DUK_LOG, | |
266 | duk__round_fixed, | |
267 | DUK_SIN, | |
268 | DUK_SQRT, | |
269 | DUK_TAN | |
270 | #endif | |
271 | }; | |
272 | ||
273 | /* order must match constants in genbuiltins.py */ | |
274 | DUK_LOCAL const duk__two_arg_func duk__two_arg_funcs[] = { | |
275 | #if defined(DUK_USE_AVOID_PLATFORM_FUNCPTRS) | |
276 | duk__atan2, | |
277 | duk__pow_fixed | |
278 | #else | |
279 | DUK_ATAN2, | |
280 | duk__pow_fixed | |
281 | #endif | |
282 | }; | |
283 | ||
284 | DUK_INTERNAL duk_ret_t duk_bi_math_object_onearg_shared(duk_context *ctx) { | |
285 | duk_small_int_t fun_idx = duk_get_current_magic(ctx); | |
286 | duk__one_arg_func fun; | |
287 | ||
288 | DUK_ASSERT(fun_idx >= 0); | |
289 | DUK_ASSERT(fun_idx < (duk_small_int_t) (sizeof(duk__one_arg_funcs) / sizeof(duk__one_arg_func))); | |
290 | fun = duk__one_arg_funcs[fun_idx]; | |
291 | duk_push_number(ctx, (duk_double_t) fun((double) duk_to_number(ctx, 0))); | |
292 | return 1; | |
293 | } | |
294 | ||
295 | DUK_INTERNAL duk_ret_t duk_bi_math_object_twoarg_shared(duk_context *ctx) { | |
296 | duk_small_int_t fun_idx = duk_get_current_magic(ctx); | |
297 | duk__two_arg_func fun; | |
298 | ||
299 | DUK_ASSERT(fun_idx >= 0); | |
300 | DUK_ASSERT(fun_idx < (duk_small_int_t) (sizeof(duk__two_arg_funcs) / sizeof(duk__two_arg_func))); | |
301 | fun = duk__two_arg_funcs[fun_idx]; | |
302 | duk_push_number(ctx, (duk_double_t) fun((double) duk_to_number(ctx, 0), (double) duk_to_number(ctx, 1))); | |
303 | return 1; | |
304 | } | |
305 | ||
306 | DUK_INTERNAL duk_ret_t duk_bi_math_object_max(duk_context *ctx) { | |
307 | return duk__math_minmax(ctx, -DUK_DOUBLE_INFINITY, duk__fmax_fixed); | |
308 | } | |
309 | ||
310 | DUK_INTERNAL duk_ret_t duk_bi_math_object_min(duk_context *ctx) { | |
311 | return duk__math_minmax(ctx, DUK_DOUBLE_INFINITY, duk__fmin_fixed); | |
312 | } | |
313 | ||
314 | DUK_INTERNAL duk_ret_t duk_bi_math_object_random(duk_context *ctx) { | |
315 | duk_push_number(ctx, (duk_double_t) duk_util_tinyrandom_get_double((duk_hthread *) ctx)); | |
316 | return 1; | |
317 | } | |
318 | ||
319 | #else /* DUK_USE_MATH_BUILTIN */ | |
320 | ||
321 | /* A stubbed built-in is useful for e.g. compilation torture testing with BCC. */ | |
322 | ||
323 | DUK_INTERNAL duk_ret_t duk_bi_math_object_onearg_shared(duk_context *ctx) { | |
324 | DUK_UNREF(ctx); | |
325 | return DUK_RET_UNIMPLEMENTED_ERROR; | |
326 | } | |
327 | ||
328 | DUK_INTERNAL duk_ret_t duk_bi_math_object_twoarg_shared(duk_context *ctx) { | |
329 | DUK_UNREF(ctx); | |
330 | return DUK_RET_UNIMPLEMENTED_ERROR; | |
331 | } | |
332 | ||
333 | DUK_INTERNAL duk_ret_t duk_bi_math_object_max(duk_context *ctx) { | |
334 | DUK_UNREF(ctx); | |
335 | return DUK_RET_UNIMPLEMENTED_ERROR; | |
336 | } | |
337 | ||
338 | DUK_INTERNAL duk_ret_t duk_bi_math_object_min(duk_context *ctx) { | |
339 | DUK_UNREF(ctx); | |
340 | return DUK_RET_UNIMPLEMENTED_ERROR; | |
341 | } | |
342 | ||
343 | DUK_INTERNAL duk_ret_t duk_bi_math_object_random(duk_context *ctx) { | |
344 | DUK_UNREF(ctx); | |
345 | return DUK_RET_UNIMPLEMENTED_ERROR; | |
346 | } | |
347 | ||
348 | #endif /* DUK_USE_MATH_BUILTIN */ |