#include <stdlib.h>
#include <stdint.h>
#include <math.h>
+#include <assert.h>
#include "../quickjs.h"
#include "../cutils.h"
JS_ThrowRangeError(ctx, "value must be an integer");
return -1;
}
- *pres = (js_int128_t)d;
- if (d != 0 && *pres == 0) {
+ if (d > 0x1p127 || d < -0x1p127) {
JS_ThrowRangeError(ctx, "value out of range");
return -1;
}
+ *pres = (js_int128_t)d;
return 0;
}
}
}
-/* Divide total_ns by divisor as a correctly-rounded double.
- |total_ns| < ~2^86 (from DURATION_MAX_SECONDS * 1e9 + subsecond parts).
- divisor is a uint64_t unit in nanoseconds, always < 2^53.
- The simplest correct method uses __float128 division. */
-static double duration_total_div(js_int128_t total_ns, uint64_t divisor)
+static int clz128(js_uint128_t a)
{
- /* Fast path: both total_ns and divisor are exactly representable as doubles,
- and IEEE 754 division is correctly rounded. */
- if (total_ns < (1LL << 53) && total_ns > -(1LL << 53)) {
- return (double)total_ns / (double)divisor;
- }
-#ifdef __SIZEOF_FLOAT128__
- return (double)((__float128)total_ns / (__float128)divisor);
-#else
- {
- js_int128_t q = total_ns / (js_int128_t)divisor;
- js_int128_t r = total_ns % (js_int128_t)divisor;
- return (double)q + (double)r / (double)divisor;
+ if ((a >> 64) != 0)
+ return clz64(a >> 64);
+ else
+ return clz64(a) + 64;
+}
+
+static js_uint128_t shr_rnd128(js_uint128_t a, int n)
+{
+ js_uint128_t addend;
+ addend = ((js_uint128_t)1 << (n - 1)) - 1 + ((a >> n) & 1);
+ return (a + addend) >> n;
+}
+
+/* Divide a by b as a correctly-rounded double. */
+static double duration_total_div(js_int128_t a, uint64_t b)
+{
+ int a_exp, b_exp, q_exp, r_exp, sgn, shift;
+ js_uint128_t a1, q, r;
+ uint64_t m, res;
+
+ if (a > -((int64_t)1 << 53) && a < ((int64_t)1 << 53) &&
+ b < ((uint64_t)1 << 53)) {
+ /* fast case */
+ return (double)(int64_t)a / (double)(int64_t)b;
}
-#endif
+
+ if (a == 0)
+ return 0;
+
+ if (a < 0) {
+ sgn = 1;
+ a1 = -a;
+ } else {
+ sgn = 0;
+ a1 = a;
+ }
+
+ a_exp = 127 - clz128(a1); /* 2^a_exp <= a < 2^(a_exp + 1) */
+ b_exp = 63 - clz64(b); /* 2^b_exp <= b < 2^(b_exp + 1) */
+
+ /* Ensure that the quotient is >= 2^54:
+ 2^(a_exp - b_exp - 1) < a/b < 2^(a_exp - b_exp + 1) */
+ shift = max_int(54 - (a_exp - b_exp - 1), 0);
+ a1 <<= shift;
+ r_exp = -shift;
+
+ q = a1 / b;
+ r = a1 % b;
+ q |= (r != 0);
+ q_exp = 127 - clz128(q);
+ assert(q_exp >= 54);
+ r_exp += q_exp - 1;
+ shift = q_exp - 52;
+ m = shr_rnd128(q, shift);
+ if (m >= ((uint64_t)1 << 53))
+ r_exp++;
+ res = ((uint64_t)sgn << 63) | ((uint64_t )(r_exp + 1024) << 52) | (m & (((uint64_t)1 << 52) - 1));
+ return uint64_as_float64(res);
}
/* NudgeToCalendarUnit-style total for ZonedDateTime relativeTo.
JSValue ti_timespec_to_bigint(JSContext *ctx, const JSTimeSpec *ts)
{
- js_int128_t total_ns = (js_int128_t)ts->secs * 1000000000LL + ts->nsecs;
+ js_int128_t total_ns = ti_timespec_to_ns(ts);
return JS_NewBigInt128(ctx, (uint64_t)total_ns, (uint64_t)(total_ns >> 64));
}
int ti_bigint_to_timespec(JSContext *ctx, JSValueConst val, JSTimeSpec *ts)
{
uint64_t low, high;
- int32_t nsecs;
int ret;
js_int128_t total_ns;
JS_ThrowRangeError(ctx, "epochNanoseconds out of range");
return -1;
}
- ts->secs = (int64_t)(total_ns / 1000000000LL);
- nsecs = (int32_t)(total_ns % 1000000000LL);
- if (nsecs < 0) {
- ts->secs -= 1;
- nsecs += 1000000000;
- }
- ts->nsecs = (uint32_t)nsecs;
+ ti_ns_to_timespec(total_ns, ts);
return 0;
}
/* 128-bit integer type: __int128 on 64-bit, _BitInt(128) on 32-bit */
#ifdef __SIZEOF_INT128__
typedef __int128 js_int128_t;
+typedef unsigned __int128 js_uint128_t;
#else
typedef _BitInt(128) js_int128_t;
+typedef unsigned _BitInt(128) js_uint128_t;
#endif
/* Temporal limits */
static inline void ti_ns_to_timespec(js_int128_t ns, JSTimeSpec *ts)
{
+ int32_t nsecs;
ts->secs = (int64_t)(ns / 1000000000LL);
- ts->nsecs = (uint32_t)(ns % 1000000000LL);
- if (ts->nsecs != 0 && ns < 0) {
- ts->nsecs += 1000000000;
+ nsecs = ns % 1000000000LL;
+ if (nsecs < 0) {
+ nsecs += 1000000000;
ts->secs -= 1;
}
+ ts->nsecs = nsecs;
}