cosmopolitan/third_party/duktape/duk_js_arith.c

138 lines
5.0 KiB
C
Raw Normal View History

2020-06-15 14:18:57 +00:00
/*
* Shared helpers for arithmetic operations
*/
#include "third_party/duktape/duk_internal.h"
/* ECMAScript modulus ('%') does not match IEEE 754 "remainder" operation
* (implemented by remainder() in C99) but does seem to match ANSI C fmod().
* Compare E5 Section 11.5.3 and "man fmod".
*/
DUK_INTERNAL double duk_js_arith_mod(double d1, double d2) {
#if defined(DUK_USE_POW_WORKAROUNDS)
/* Specific fixes to common fmod() implementation issues:
* - test-bug-mingw-math-issues.js
*/
if (DUK_ISINF(d2)) {
if (DUK_ISINF(d1)) {
return DUK_DOUBLE_NAN;
} else {
return d1;
}
} else if (duk_double_equals(d1, 0.0)) {
/* d1 +/-0 is returned as is (preserving sign) except when
* d2 is zero or NaN.
*/
if (duk_double_equals(d2, 0.0) || DUK_ISNAN(d2)) {
return DUK_DOUBLE_NAN;
} else {
return d1;
}
}
#else
/* Some ISO C assumptions. */
DUK_ASSERT(duk_double_equals(DUK_FMOD(1.0, DUK_DOUBLE_INFINITY), 1.0));
DUK_ASSERT(duk_double_equals(DUK_FMOD(-1.0, DUK_DOUBLE_INFINITY), -1.0));
DUK_ASSERT(duk_double_equals(DUK_FMOD(1.0, -DUK_DOUBLE_INFINITY), 1.0));
DUK_ASSERT(duk_double_equals(DUK_FMOD(-1.0, -DUK_DOUBLE_INFINITY), -1.0));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-DUK_DOUBLE_INFINITY, DUK_DOUBLE_INFINITY)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-DUK_DOUBLE_INFINITY, -DUK_DOUBLE_INFINITY)));
DUK_ASSERT(duk_double_equals(DUK_FMOD(0.0, 1.0), 0.0) && DUK_SIGNBIT(DUK_FMOD(0.0, 1.0)) == 0);
DUK_ASSERT(duk_double_equals(DUK_FMOD(-0.0, 1.0), 0.0) && DUK_SIGNBIT(DUK_FMOD(-0.0, 1.0)) != 0);
DUK_ASSERT(duk_double_equals(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY), 0.0) && DUK_SIGNBIT(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY)) == 0);
DUK_ASSERT(duk_double_equals(DUK_FMOD(-0.0, DUK_DOUBLE_INFINITY), 0.0) && DUK_SIGNBIT(DUK_FMOD(-0.0, DUK_DOUBLE_INFINITY)) != 0);
DUK_ASSERT(duk_double_equals(DUK_FMOD(0.0, -DUK_DOUBLE_INFINITY), 0.0) && DUK_SIGNBIT(DUK_FMOD(0.0, DUK_DOUBLE_INFINITY)) == 0);
DUK_ASSERT(duk_double_equals(DUK_FMOD(-0.0, -DUK_DOUBLE_INFINITY), 0.0) && DUK_SIGNBIT(DUK_FMOD(-0.0, -DUK_DOUBLE_INFINITY)) != 0);
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(0.0, 0.0)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-0.0, 0.0)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(0.0, -0.0)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-0.0, -0.0)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(0.0, DUK_DOUBLE_NAN)));
DUK_ASSERT(DUK_ISNAN(DUK_FMOD(-0.0, DUK_DOUBLE_NAN)));
#endif
return (duk_double_t) DUK_FMOD((double) d1, (double) d2);
}
/* Shared helper for Math.pow() and exponentiation operator. */
DUK_INTERNAL double duk_js_arith_pow(double x, double y) {
/* The ANSI C pow() semantics differ from ECMAScript.
*
* E.g. when x==1 and y is +/- infinite, the ECMAScript required
* result is NaN, while at least Linux pow() returns 1.
*/
duk_small_int_t cx, cy, sx;
DUK_UNREF(cx);
DUK_UNREF(sx);
cy = (duk_small_int_t) DUK_FPCLASSIFY(y);
if (cy == DUK_FP_NAN) {
goto ret_nan;
}
if (duk_double_equals(DUK_FABS(x), 1.0) && cy == DUK_FP_INFINITE) {
goto ret_nan;
}
#if defined(DUK_USE_POW_WORKAROUNDS)
/* Specific fixes to common pow() implementation issues:
* - test-bug-netbsd-math-pow.js: NetBSD 6.0 on x86 (at least)
* - test-bug-mingw-math-issues.js
*/
cx = (duk_small_int_t) DUK_FPCLASSIFY(x);
if (cx == DUK_FP_ZERO && y < 0.0) {
sx = (duk_small_int_t) DUK_SIGNBIT(x);
if (sx == 0) {
/* Math.pow(+0,y) should be Infinity when y<0. NetBSD pow()
* returns -Infinity instead when y is <0 and finite. The
* if-clause also catches y == -Infinity (which works even
* without the fix).
*/
return DUK_DOUBLE_INFINITY;
} else {
/* Math.pow(-0,y) where y<0 should be:
* - -Infinity if y<0 and an odd integer
* - Infinity if y<0 but not an odd integer
* NetBSD pow() returns -Infinity for all finite y<0. The
* if-clause also catches y == -Infinity (which works even
* without the fix).
*/
/* fmod() return value has same sign as input (negative) so
* the result here will be in the range ]-2,0], -1 indicates
* odd. If x is -Infinity, NaN is returned and the odd check
* always concludes "not odd" which results in desired outcome.
*/
double tmp = DUK_FMOD(y, 2);
if (tmp == -1.0) {
return -DUK_DOUBLE_INFINITY;
} else {
/* Not odd, or y == -Infinity */
return DUK_DOUBLE_INFINITY;
}
}
} else if (cx == DUK_FP_NAN) {
if (duk_double_equals(y, 0.0)) {
/* NaN ** +/- 0 should always be 1, but is NaN on
* at least some Cygwin/MinGW versions.
*/
return 1.0;
}
}
#else
/* Some ISO C assumptions. */
DUK_ASSERT(duk_double_equals(DUK_POW(DUK_DOUBLE_NAN, 0.0), 1.0));
DUK_ASSERT(DUK_ISINF(DUK_POW(0.0, -1.0)) && DUK_SIGNBIT(DUK_POW(0.0, -1.0)) == 0);
DUK_ASSERT(DUK_ISINF(DUK_POW(-0.0, -2.0)) && DUK_SIGNBIT(DUK_POW(-0.0, -2.0)) == 0);
DUK_ASSERT(DUK_ISINF(DUK_POW(-0.0, -3.0)) && DUK_SIGNBIT(DUK_POW(-0.0, -3.0)) != 0);
#endif
return DUK_POW(x, y);
ret_nan:
return DUK_DOUBLE_NAN;
}