5 #include "duk_internal.h"
7 #if defined(DUK_USE_MATH_BUILTIN)
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.
14 * Typing here is intentionally 'double' wherever values interact with
15 * the standard library APIs.
18 typedef double (*duk__one_arg_func
)(double);
19 typedef double (*duk__two_arg_func
)(double, double);
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
);
24 duk_double_t res
= initial
;
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().
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.
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
;
44 res
= (duk_double_t
) min_max(res
, (double) t
);
48 duk_push_number(ctx
, res
);
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.
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) {
64 #ifdef DUK_USE_MATH_FMIN
65 return DUK_FMIN(x
, y
);
67 return (x
< y
? x
: y
);
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.
75 if (x
== 0 && y
== 0) {
76 if (DUK_SIGNBIT(x
) == 0 || DUK_SIGNBIT(y
) == 0) {
82 #ifdef DUK_USE_MATH_FMAX
83 return DUK_FMAX(x
, y
);
85 return (x
> y
? x
: y
);
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.
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().
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
) {
104 * x is finite and non-zero
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
118 if (x
>= -0.5 && x
< 0.5) {
119 /* +0.5 is handled by floor, this is on purpose */
127 return DUK_FLOOR(x
+ 0.5);
130 DUK_LOCAL
double duk__pow_fixed(double x
, double y
) {
131 /* The ANSI C pow() semantics differ from Ecmascript.
133 * E.g. when x==1 and y is +/- infinite, the Ecmascript required
134 * result is NaN, while at least Linux pow() returns 1.
137 duk_small_int_t cx
, cy
, sx
;
141 cy
= (duk_small_int_t
) DUK_FPCLASSIFY(y
);
143 if (cy
== DUK_FP_NAN
) {
146 if (DUK_FABS(x
) == 1.0 && cy
== DUK_FP_INFINITE
) {
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
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
);
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
163 return DUK_DOUBLE_INFINITY
;
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
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.
178 double tmp
= DUK_FMOD(y
, 2);
180 return -DUK_DOUBLE_INFINITY
;
182 /* Not odd, or y == -Infinity */
183 return DUK_DOUBLE_INFINITY
;
188 return DUK_POW(x
, y
);
191 return DUK_DOUBLE_NAN
;
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.
198 #if defined(DUK_USE_AVOID_PLATFORM_FUNCPTRS)
199 DUK_LOCAL
double duk__fabs(double x
) {
202 DUK_LOCAL
double duk__acos(double x
) {
205 DUK_LOCAL
double duk__asin(double x
) {
208 DUK_LOCAL
double duk__atan(double x
) {
211 DUK_LOCAL
double duk__ceil(double x
) {
214 DUK_LOCAL
double duk__cos(double x
) {
217 DUK_LOCAL
double duk__exp(double x
) {
220 DUK_LOCAL
double duk__floor(double x
) {
223 DUK_LOCAL
double duk__log(double x
) {
226 DUK_LOCAL
double duk__sin(double x
) {
229 DUK_LOCAL
double duk__sqrt(double x
) {
232 DUK_LOCAL
double duk__tan(double x
) {
235 DUK_LOCAL
double duk__atan2(double x
, double y
) {
236 return DUK_ATAN2(x
, y
);
238 #endif /* DUK_USE_AVOID_PLATFORM_FUNCPTRS */
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)
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)
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
;
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)));
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
;
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)));
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
);
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
);
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
));
319 #else /* DUK_USE_MATH_BUILTIN */
321 /* A stubbed built-in is useful for e.g. compilation torture testing with BCC. */
323 DUK_INTERNAL duk_ret_t
duk_bi_math_object_onearg_shared(duk_context
*ctx
) {
325 return DUK_RET_UNIMPLEMENTED_ERROR
;
328 DUK_INTERNAL duk_ret_t
duk_bi_math_object_twoarg_shared(duk_context
*ctx
) {
330 return DUK_RET_UNIMPLEMENTED_ERROR
;
333 DUK_INTERNAL duk_ret_t
duk_bi_math_object_max(duk_context
*ctx
) {
335 return DUK_RET_UNIMPLEMENTED_ERROR
;
338 DUK_INTERNAL duk_ret_t
duk_bi_math_object_min(duk_context
*ctx
) {
340 return DUK_RET_UNIMPLEMENTED_ERROR
;
343 DUK_INTERNAL duk_ret_t
duk_bi_math_object_random(duk_context
*ctx
) {
345 return DUK_RET_UNIMPLEMENTED_ERROR
;
348 #endif /* DUK_USE_MATH_BUILTIN */