343 lines
8.7 KiB
C
343 lines
8.7 KiB
C
/*
|
|
* IEEE double helpers.
|
|
*/
|
|
|
|
#include "duk_internal.h"
|
|
|
|
DUK_INTERNAL duk_bool_t duk_double_is_anyinf(duk_double_t x) {
|
|
duk_double_union du;
|
|
du.d = x;
|
|
return DUK_DBLUNION_IS_ANYINF(&du);
|
|
}
|
|
|
|
DUK_INTERNAL duk_bool_t duk_double_is_posinf(duk_double_t x) {
|
|
duk_double_union du;
|
|
du.d = x;
|
|
return DUK_DBLUNION_IS_POSINF(&du);
|
|
}
|
|
|
|
DUK_INTERNAL duk_bool_t duk_double_is_neginf(duk_double_t x) {
|
|
duk_double_union du;
|
|
du.d = x;
|
|
return DUK_DBLUNION_IS_NEGINF(&du);
|
|
}
|
|
|
|
DUK_INTERNAL duk_bool_t duk_double_is_nan(duk_double_t x) {
|
|
duk_double_union du;
|
|
du.d = x;
|
|
/* Assumes we're dealing with a Duktape internal NaN which is
|
|
* NaN normalized if duk_tval requires it.
|
|
*/
|
|
DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
|
|
return DUK_DBLUNION_IS_NAN(&du);
|
|
}
|
|
|
|
DUK_INTERNAL duk_bool_t duk_double_is_nan_or_zero(duk_double_t x) {
|
|
duk_double_union du;
|
|
du.d = x;
|
|
/* Assumes we're dealing with a Duktape internal NaN which is
|
|
* NaN normalized if duk_tval requires it.
|
|
*/
|
|
DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
|
|
return DUK_DBLUNION_IS_NAN(&du) || DUK_DBLUNION_IS_ANYZERO(&du);
|
|
}
|
|
|
|
DUK_INTERNAL duk_bool_t duk_double_is_nan_or_inf(duk_double_t x) {
|
|
duk_double_union du;
|
|
du.d = x;
|
|
/* If exponent is 0x7FF the argument is either a NaN or an
|
|
* infinity. We don't need to check any other fields.
|
|
*/
|
|
#if defined(DUK_USE_64BIT_OPS)
|
|
#if defined(DUK_USE_DOUBLE_ME)
|
|
return (du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x000000007ff00000)) == DUK_U64_CONSTANT(0x000000007ff00000);
|
|
#else
|
|
return (du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7ff0000000000000)) == DUK_U64_CONSTANT(0x7ff0000000000000);
|
|
#endif
|
|
#else
|
|
return (du.ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL) == 0x7ff00000UL;
|
|
#endif
|
|
}
|
|
|
|
DUK_INTERNAL duk_bool_t duk_double_is_nan_zero_inf(duk_double_t x) {
|
|
duk_double_union du;
|
|
#if defined(DUK_USE_64BIT_OPS)
|
|
duk_uint64_t t;
|
|
#else
|
|
duk_uint32_t t;
|
|
#endif
|
|
du.d = x;
|
|
#if defined(DUK_USE_64BIT_OPS)
|
|
#if defined(DUK_USE_DOUBLE_ME)
|
|
t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x000000007ff00000);
|
|
if (t == DUK_U64_CONSTANT(0x0000000000000000)) {
|
|
t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x0000000080000000);
|
|
return t == 0;
|
|
}
|
|
if (t == DUK_U64_CONSTANT(0x000000007ff00000)) {
|
|
return 1;
|
|
}
|
|
#else
|
|
t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7ff0000000000000);
|
|
if (t == DUK_U64_CONSTANT(0x0000000000000000)) {
|
|
t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x8000000000000000);
|
|
return t == 0;
|
|
}
|
|
if (t == DUK_U64_CONSTANT(0x7ff0000000000000)) {
|
|
return 1;
|
|
}
|
|
#endif
|
|
#else
|
|
t = du.ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL;
|
|
if (t == 0x00000000UL) {
|
|
return DUK_DBLUNION_IS_ANYZERO(&du);
|
|
}
|
|
if (t == 0x7ff00000UL) {
|
|
return 1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
DUK_INTERNAL duk_small_uint_t duk_double_signbit(duk_double_t x) {
|
|
duk_double_union du;
|
|
du.d = x;
|
|
return (duk_small_uint_t) DUK_DBLUNION_GET_SIGNBIT(&du);
|
|
}
|
|
|
|
DUK_INTERNAL duk_double_t duk_double_trunc_towards_zero(duk_double_t x) {
|
|
/* XXX: optimize */
|
|
duk_small_uint_t s = duk_double_signbit(x);
|
|
x = DUK_FLOOR(DUK_FABS(x)); /* truncate towards zero */
|
|
if (s) {
|
|
x = -x;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
DUK_INTERNAL duk_bool_t duk_double_same_sign(duk_double_t x, duk_double_t y) {
|
|
duk_double_union du1;
|
|
duk_double_union du2;
|
|
du1.d = x;
|
|
du2.d = y;
|
|
|
|
return (((du1.ui[DUK_DBL_IDX_UI0] ^ du2.ui[DUK_DBL_IDX_UI0]) & 0x80000000UL) == 0);
|
|
}
|
|
|
|
DUK_INTERNAL duk_double_t duk_double_fmin(duk_double_t x, duk_double_t y) {
|
|
/* Doesn't replicate fmin() behavior exactly: for fmin() if one
|
|
* argument is a NaN, the other argument should be returned.
|
|
* Duktape doesn't rely on this behavior so the replacement can
|
|
* be simplified.
|
|
*/
|
|
return (x < y ? x : y);
|
|
}
|
|
|
|
DUK_INTERNAL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y) {
|
|
/* Doesn't replicate fmax() behavior exactly: for fmax() if one
|
|
* argument is a NaN, the other argument should be returned.
|
|
* Duktape doesn't rely on this behavior so the replacement can
|
|
* be simplified.
|
|
*/
|
|
return (x > y ? x : y);
|
|
}
|
|
|
|
DUK_INTERNAL duk_bool_t duk_double_is_finite(duk_double_t x) {
|
|
return !duk_double_is_nan_or_inf(x);
|
|
}
|
|
|
|
DUK_INTERNAL duk_bool_t duk_double_is_integer(duk_double_t x) {
|
|
if (duk_double_is_nan_or_inf(x)) {
|
|
return 0;
|
|
} else {
|
|
return duk_double_equals(duk_js_tointeger_number(x), x);
|
|
}
|
|
}
|
|
|
|
DUK_INTERNAL duk_bool_t duk_double_is_safe_integer(duk_double_t x) {
|
|
/* >>> 2**53-1
|
|
* 9007199254740991
|
|
*/
|
|
return duk_double_is_integer(x) && DUK_FABS(x) <= 9007199254740991.0;
|
|
}
|
|
|
|
/* Check whether a duk_double_t is a whole number in the 32-bit range (reject
|
|
* negative zero), and if so, return a duk_int32_t.
|
|
* For compiler use: don't allow negative zero as it will cause trouble with
|
|
* LDINT+LDINTX, positive zero is OK.
|
|
*/
|
|
DUK_INTERNAL duk_bool_t duk_is_whole_get_int32_nonegzero(duk_double_t x, duk_int32_t *ival) {
|
|
duk_int32_t t;
|
|
|
|
t = duk_double_to_int32_t(x);
|
|
if (!duk_double_equals((duk_double_t) t, x)) {
|
|
return 0;
|
|
}
|
|
if (t == 0) {
|
|
duk_double_union du;
|
|
du.d = x;
|
|
if (DUK_DBLUNION_HAS_SIGNBIT(&du)) {
|
|
return 0;
|
|
}
|
|
}
|
|
*ival = t;
|
|
return 1;
|
|
}
|
|
|
|
/* Check whether a duk_double_t is a whole number in the 32-bit range, and if
|
|
* so, return a duk_int32_t.
|
|
*/
|
|
DUK_INTERNAL duk_bool_t duk_is_whole_get_int32(duk_double_t x, duk_int32_t *ival) {
|
|
duk_int32_t t;
|
|
|
|
t = duk_double_to_int32_t(x);
|
|
if (!duk_double_equals((duk_double_t) t, x)) {
|
|
return 0;
|
|
}
|
|
*ival = t;
|
|
return 1;
|
|
}
|
|
|
|
/* Division: division by zero is undefined behavior (and may in fact trap)
|
|
* so it needs special handling for portability.
|
|
*/
|
|
|
|
DUK_INTERNAL DUK_INLINE duk_double_t duk_double_div(duk_double_t x, duk_double_t y) {
|
|
#if !defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR)
|
|
if (DUK_UNLIKELY(duk_double_equals(y, 0.0) != 0)) {
|
|
/* In C99+ division by zero is undefined behavior so
|
|
* avoid it entirely. Hopefully the compiler is
|
|
* smart enough to avoid emitting any actual code
|
|
* because almost all practical platforms behave as
|
|
* expected.
|
|
*/
|
|
if (x > 0.0) {
|
|
if (DUK_SIGNBIT(y)) {
|
|
return -DUK_DOUBLE_INFINITY;
|
|
} else {
|
|
return DUK_DOUBLE_INFINITY;
|
|
}
|
|
} else if (x < 0.0) {
|
|
if (DUK_SIGNBIT(y)) {
|
|
return DUK_DOUBLE_INFINITY;
|
|
} else {
|
|
return -DUK_DOUBLE_INFINITY;
|
|
}
|
|
} else {
|
|
/* +/- 0, NaN */
|
|
return DUK_DOUBLE_NAN;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return x / y;
|
|
}
|
|
|
|
/* Double and float byteorder changes. */
|
|
|
|
DUK_INTERNAL DUK_INLINE void duk_dblunion_host_to_little(duk_double_union *u) {
|
|
#if defined(DUK_USE_DOUBLE_LE)
|
|
/* HGFEDCBA -> HGFEDCBA */
|
|
DUK_UNREF(u);
|
|
#elif defined(DUK_USE_DOUBLE_ME)
|
|
duk_uint32_t a, b;
|
|
|
|
/* DCBAHGFE -> HGFEDCBA */
|
|
a = u->ui[0];
|
|
b = u->ui[1];
|
|
u->ui[0] = b;
|
|
u->ui[1] = a;
|
|
#elif defined(DUK_USE_DOUBLE_BE)
|
|
/* ABCDEFGH -> HGFEDCBA */
|
|
#if defined(DUK_USE_64BIT_OPS)
|
|
u->ull[0] = DUK_BSWAP64(u->ull[0]);
|
|
#else
|
|
duk_uint32_t a, b;
|
|
|
|
a = u->ui[0];
|
|
b = u->ui[1];
|
|
u->ui[0] = DUK_BSWAP32(b);
|
|
u->ui[1] = DUK_BSWAP32(a);
|
|
#endif
|
|
#else
|
|
#error internal error
|
|
#endif
|
|
}
|
|
|
|
DUK_INTERNAL DUK_INLINE void duk_dblunion_little_to_host(duk_double_union *u) {
|
|
duk_dblunion_host_to_little(u);
|
|
}
|
|
|
|
DUK_INTERNAL DUK_INLINE void duk_dblunion_host_to_big(duk_double_union *u) {
|
|
#if defined(DUK_USE_DOUBLE_LE)
|
|
/* HGFEDCBA -> ABCDEFGH */
|
|
#if defined(DUK_USE_64BIT_OPS)
|
|
u->ull[0] = DUK_BSWAP64(u->ull[0]);
|
|
#else
|
|
duk_uint32_t a, b;
|
|
|
|
a = u->ui[0];
|
|
b = u->ui[1];
|
|
u->ui[0] = DUK_BSWAP32(b);
|
|
u->ui[1] = DUK_BSWAP32(a);
|
|
#endif
|
|
#elif defined(DUK_USE_DOUBLE_ME)
|
|
duk_uint32_t a, b;
|
|
|
|
/* DCBAHGFE -> ABCDEFGH */
|
|
a = u->ui[0];
|
|
b = u->ui[1];
|
|
u->ui[0] = DUK_BSWAP32(a);
|
|
u->ui[1] = DUK_BSWAP32(b);
|
|
#elif defined(DUK_USE_DOUBLE_BE)
|
|
/* ABCDEFGH -> ABCDEFGH */
|
|
DUK_UNREF(u);
|
|
#else
|
|
#error internal error
|
|
#endif
|
|
}
|
|
|
|
DUK_INTERNAL DUK_INLINE void duk_dblunion_big_to_host(duk_double_union *u) {
|
|
duk_dblunion_host_to_big(u);
|
|
}
|
|
|
|
DUK_INTERNAL DUK_INLINE void duk_fltunion_host_to_big(duk_float_union *u) {
|
|
#if defined(DUK_USE_DOUBLE_LE) || defined(DUK_USE_DOUBLE_ME)
|
|
/* DCBA -> ABCD */
|
|
u->ui[0] = DUK_BSWAP32(u->ui[0]);
|
|
#elif defined(DUK_USE_DOUBLE_BE)
|
|
/* ABCD -> ABCD */
|
|
DUK_UNREF(u);
|
|
#else
|
|
#error internal error
|
|
#endif
|
|
}
|
|
|
|
DUK_INTERNAL DUK_INLINE void duk_fltunion_big_to_host(duk_float_union *u) {
|
|
duk_fltunion_host_to_big(u);
|
|
}
|
|
|
|
/* Comparison: ensures comparison operates on exactly correct types, avoiding
|
|
* some floating point comparison pitfalls (e.g. atan2() assertions failed on
|
|
* -m32 with direct comparison, even with explicit casts).
|
|
*/
|
|
#if defined(DUK_USE_GCC_PRAGMAS)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
|
#elif defined(DUK_USE_CLANG_PRAGMAS)
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wfloat-equal"
|
|
#endif
|
|
|
|
DUK_INTERNAL DUK_ALWAYS_INLINE duk_bool_t duk_double_equals(duk_double_t x, duk_double_t y) {
|
|
return x == y;
|
|
}
|
|
|
|
DUK_INTERNAL DUK_ALWAYS_INLINE duk_bool_t duk_float_equals(duk_float_t x, duk_float_t y) {
|
|
return x == y;
|
|
}
|
|
#if defined(DUK_USE_GCC_PRAGMAS)
|
|
#pragma GCC diagnostic pop
|
|
#elif defined(DUK_USE_CLANG_PRAGMAS)
|
|
#pragma clang diagnostic pop
|
|
#endif
|