Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,6 @@ set(qjs_sources
libregexp.c
libunicode.c
quickjs.c
xsum.c
)

if(QJS_BUILD_LIBC)
Expand Down
4 changes: 0 additions & 4 deletions amalgam.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ const quickjs_h = loadFile("quickjs.h")
const quickjs_libc_c = loadFile("quickjs-libc.c")
const quickjs_libc_h = loadFile("quickjs-libc.h")
const quickjs_opcode_h = loadFile("quickjs-opcode.h")
const xsum_c = loadFile("xsum.c")
const xsum_h = loadFile("xsum.h")
const gen_builtin_array_fromasync_h = loadFile("builtin-array-fromasync.h")

let source = "#if defined(QJS_BUILD_LIBC) && defined(__linux__) && !defined(_GNU_SOURCE)\n"
Expand All @@ -32,14 +30,12 @@ let source = "#if defined(QJS_BUILD_LIBC) && defined(__linux__) && !defined(_GNU
+ libunicode_h // exports lre_is_id_start, used by libregexp.h
+ libregexp_h
+ libunicode_table_h
+ xsum_h
+ quickjs_h
+ quickjs_c
+ cutils_c
+ dtoa_c
+ libregexp_c
+ libunicode_c
+ xsum_c
+ "#ifdef QJS_BUILD_LIBC\n"
+ quickjs_libc_h
+ quickjs_libc_c
Expand Down
1 change: 0 additions & 1 deletion fuzz.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include "cutils.c"
#include "libregexp.c"
#include "libunicode.c"
#include "xsum.c"
#include <stdlib.h>

int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len)
Expand Down
1 change: 0 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ qjs_srcs = files(
'libregexp.c',
'libunicode.c',
'quickjs.c',
'xsum.c'
)
qjs_hdrs = files(
'quickjs.h',
Expand Down
279 changes: 217 additions & 62 deletions quickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
#include "list.h"
#include "quickjs.h"
#include "libregexp.h"
#include "xsum.h"
#include "dtoa.h"

#if defined(EMSCRIPTEN) || defined(_MSC_VER)
Expand Down Expand Up @@ -11998,7 +11997,7 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
bool buf_allocated = false;
JSValue val;
JSATODTempMem atod_mem;

/* optional separator between digits */
sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
has_legacy_octal = false;
Expand Down Expand Up @@ -12799,7 +12798,7 @@ static JSValue js_dtoa2(JSContext *ctx,
JSValue res;
JSDTOATempMem dtoa_mem;
len_max = js_dtoa_max_len(d, radix, n_digits, flags);

/* longer buffer may be used if radix != 10 */
if (len_max > sizeof(static_buf) - 1) {
tmp_buf = js_malloc(ctx, len_max + 1);
Expand Down Expand Up @@ -44667,93 +44666,249 @@ static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,
return js_int32(r);
}

typedef enum SumPreciseStateEnum {
/* we add one extra limb to avoid having to test for overflows during the sum */
#define SUM_PRECISE_ACC_LEN 34

typedef enum {
SUM_PRECISE_STATE_MINUS_ZERO,
SUM_PRECISE_STATE_NOT_A_NUMBER,
SUM_PRECISE_STATE_MINUS_INFINITY,
SUM_PRECISE_STATE_PLUS_INFINITY,
SUM_PRECISE_STATE_FINITE,
SUM_PRECISE_STATE_INFINITY,
SUM_PRECISE_STATE_MINUS_INFINITY, /* must be after SUM_PRECISE_STATE_INFINITY */
SUM_PRECISE_STATE_NAN, /* must be after SUM_PRECISE_STATE_MINUS_INFINITY */
} SumPreciseStateEnum;

typedef struct {
uint64_t acc[SUM_PRECISE_ACC_LEN];
int n_limbs; /* acc is not necessarily normalized */
SumPreciseStateEnum state;
} SumPreciseState;

static void sum_precise_init(SumPreciseState *s)
{
s->state = SUM_PRECISE_STATE_MINUS_ZERO;
s->acc[0] = 0;
s->n_limbs = 1;
}

#define ADDC64(res, carry_out, op1, op2, carry_in) \
do { \
uint64_t __v, __a, __k, __k1; \
__v = (op1); \
__a = __v + (op2); \
__k1 = __a < __v; \
__k = (carry_in); \
__a = __a + __k; \
carry_out = (__a < __k) | __k1; \
res = __a; \
} while (0)

static void sum_precise_add(SumPreciseState *s, double d)
{
uint64_t a, m, a0, carry, acc_sign, a_sign;
int sgn, e, p, n, i;
unsigned shift;

a = float64_as_uint64(d);
sgn = a >> 63;
e = (a >> 52) & ((1 << 11) - 1);
m = a & (((uint64_t)1 << 52) - 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a little more legible as:

Suggested change
m = a & (((uint64_t)1 << 52) - 1);
m = a & ((1ULL << 52) - 1);

Although I can't fault you for being precise with types. Consider it just a suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the rest of the code uses the uint64_t pattern too, so I think it's better to stick to it.

if (unlikely(e == 2047)) {
if (m == 0) {
/* +/- infinity */
if (s->state == SUM_PRECISE_STATE_NAN ||
(s->state == SUM_PRECISE_STATE_MINUS_INFINITY && !sgn) ||
(s->state == SUM_PRECISE_STATE_INFINITY && sgn)) {
s->state = SUM_PRECISE_STATE_NAN;
} else {
s->state = SUM_PRECISE_STATE_INFINITY + sgn;
}
} else {
/* NaN */
s->state = SUM_PRECISE_STATE_NAN;
}
} else if (e == 0) {
if (likely(m == 0)) {
/* zero */
if (s->state == SUM_PRECISE_STATE_MINUS_ZERO && !sgn)
s->state = SUM_PRECISE_STATE_FINITE;
} else {
/* subnormal */
p = 0;
shift = 0;
goto add;
}
} else {
m |= (uint64_t)1 << 52;
shift = e - 1;
p = shift / 64;
/* 'p' is the position of a0 in acc */
shift %= 64;
add:
if (s->state >= SUM_PRECISE_STATE_INFINITY)
return;
s->state = SUM_PRECISE_STATE_FINITE;
n = s->n_limbs;

acc_sign = (int64_t)s->acc[n - 1] >> 63;

/* sign extend acc */
for(i = n; i <= p; i++)
s->acc[i] = acc_sign;

carry = sgn;
a_sign = -sgn;
a0 = m << shift;
ADDC64(s->acc[p], carry, s->acc[p], a0 ^ a_sign, carry);
if (shift >= 12) {
p++;
if (p >= n)
s->acc[p] = acc_sign;
a0 = m >> (64 - shift);
ADDC64(s->acc[p], carry, s->acc[p], a0 ^ a_sign, carry);
}
p++;
if (p >= n) {
n = p;
} else {
/* carry */
for(i = p; i < n; i++) {
/* if 'a' positive: stop condition: carry = 0.
if 'a' negative: stop condition: carry = 1. */
if (carry == sgn)
goto done;
ADDC64(s->acc[i], carry, s->acc[i], a_sign, carry);
}
}

/* extend the accumulator if needed */
a0 = carry + acc_sign + a_sign;
/* -1 <= a0 <= 1 (if both acc and a are negative, carry is set) */
if (a0 != ((int64_t)s->acc[n - 1] >> 63)) {
s->acc[n++] = a0;
}
done:
s->n_limbs = n;
}
}

static double sum_precise_get_result(SumPreciseState *s)
{
int n, shift, e, p, is_neg, i;
uint64_t m, addend, carry;

if (s->state != SUM_PRECISE_STATE_FINITE) {
switch(s->state) {
default:
case SUM_PRECISE_STATE_MINUS_ZERO:
return -0.0;
case SUM_PRECISE_STATE_INFINITY:
return INFINITY;
case SUM_PRECISE_STATE_MINUS_INFINITY:
return -INFINITY;
case SUM_PRECISE_STATE_NAN:
return NAN;
}
}

/* extract the sign and absolute value */
n = s->n_limbs;
is_neg = s->acc[n - 1] >> 63;
if (is_neg) {
/* acc = -acc */
carry = 1;
for(i = 0; i < n; i++) {
ADDC64(s->acc[i], carry, ~s->acc[i], 0, carry);
}
}
/* normalize */
while (n > 0 && s->acc[n - 1] == 0)
n--;
/* zero result. The spec tells it is always positive in the finite case */
if (n == 0)
return 0.0;
/* subnormal case */
if (n == 1 && s->acc[0] < ((uint64_t)1 << 52))
return uint64_as_float64(((uint64_t)is_neg << 63) | s->acc[0]);
/* normal case */
e = n * 64;
p = n - 1;
m = s->acc[p];
shift = clz64(m);
e = e - shift - 52;
if (shift != 0) {
m <<= shift;
if (p > 0) {
int shift1;
uint64_t nz;
p--;
shift1 = 64 - shift;
nz = s->acc[p] & (((uint64_t)1 << shift1) - 1);
m = m | (s->acc[p] >> shift1) | (nz != 0);
}
}
if ((m & ((1 << 10) - 1)) == 0) {
/* see if the LSB part is non zero for the final rounding */
while (p > 0) {
p--;
if (s->acc[p] != 0) {
m |= 1;
break;
}
}
}
/* rounding to nearest with ties to even */
addend = (1 << 10) - 1 + ((m >> 11) & 1);
m = (m + addend) >> 11;
/* handle overflow in the rounding */
if (m == 0)
e++;
if (unlikely(e >= 2047)) {
/* infinity */
return uint64_as_float64(((uint64_t)is_neg << 63) | ((uint64_t)2047 << 52));
} else {
m &= (((uint64_t)1 << 52) - 1);
return uint64_as_float64(((uint64_t)is_neg << 63) | ((uint64_t)e << 52) | m);
}
}

static JSValue js_math_sumPrecise(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue iter, next, item, ret;
uint32_t tag;
int done;
double d;
xsum_small_accumulator acc;
SumPreciseStateEnum state;
SumPreciseState s_s, *s = &s_s;

iter = JS_GetIterator(ctx, argv[0], /*async*/false);
iter = JS_GetIterator(ctx, argv[0], /*is_async*/false);
if (JS_IsException(iter))
return JS_EXCEPTION;
ret = JS_EXCEPTION;
next = JS_GetProperty(ctx, iter, JS_ATOM_next);
if (JS_IsException(next))
goto fail;
xsum_small_init(&acc);
state = SUM_PRECISE_STATE_MINUS_ZERO;
sum_precise_init(s);
for (;;) {
item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
if (JS_IsException(item))
goto fail;
if (done)
break; // item == JS_UNDEFINED
switch (JS_VALUE_GET_TAG(item)) {
default:
break;
tag = JS_VALUE_GET_TAG(item);
if (JS_TAG_IS_FLOAT64(tag)) {
d = JS_VALUE_GET_FLOAT64(item);
} else if (tag == JS_TAG_INT) {
d = JS_VALUE_GET_INT(item);
} else {
JS_FreeValue(ctx, item);
JS_ThrowTypeError(ctx, "not a number");
JS_IteratorClose(ctx, iter, /*is_exception_pending*/true);
goto fail;
case JS_TAG_INT:
d = JS_VALUE_GET_INT(item);
break;
case JS_TAG_FLOAT64:
d = JS_VALUE_GET_FLOAT64(item);
break;
}

if (state != SUM_PRECISE_STATE_NOT_A_NUMBER) {
if (isnan(d))
state = SUM_PRECISE_STATE_NOT_A_NUMBER;
else if (!isfinite(d) && d > 0.0)
if (state == SUM_PRECISE_STATE_MINUS_INFINITY)
state = SUM_PRECISE_STATE_NOT_A_NUMBER;
else
state = SUM_PRECISE_STATE_PLUS_INFINITY;
else if (!isfinite(d) && d < 0.0)
if (state == SUM_PRECISE_STATE_PLUS_INFINITY)
state = SUM_PRECISE_STATE_NOT_A_NUMBER;
else
state = SUM_PRECISE_STATE_MINUS_INFINITY;
else if (!(d == 0.0 && signbit(d)) && (state == SUM_PRECISE_STATE_MINUS_ZERO || state == SUM_PRECISE_STATE_FINITE)) {
state = SUM_PRECISE_STATE_FINITE;
xsum_small_add1(&acc, d);
}
}
sum_precise_add(s, d);
}

switch (state) {
case SUM_PRECISE_STATE_NOT_A_NUMBER:
d = NAN;
break;
case SUM_PRECISE_STATE_MINUS_INFINITY:
d = -INFINITY;
break;
case SUM_PRECISE_STATE_PLUS_INFINITY:
d = INFINITY;
break;
case SUM_PRECISE_STATE_MINUS_ZERO:
d = -0.0;
break;
case SUM_PRECISE_STATE_FINITE:
d = xsum_small_round(&acc);
break;
default:
abort();
}
ret = js_float64(d);
ret = js_float64(sum_precise_get_result(s));
fail:
JS_IteratorClose(ctx, iter, JS_IsException(ret));
JS_FreeValue(ctx, iter);
JS_FreeValue(ctx, next);
return ret;
Expand Down
7 changes: 4 additions & 3 deletions tests/test_builtin.js
Original file line number Diff line number Diff line change
Expand Up @@ -392,10 +392,11 @@ function test_math()
assert(Math.imul((-2)**31, (-2)**31), 0);
assert(Math.imul(2**31-1, 2**31-1), 1);
assert(Math.fround(0.1), 0.10000000149011612);
assert(Math.hypot() == 0);
assert(Math.hypot(-2) == 2);
assert(Math.hypot(3, 4) == 5);
assert(Math.hypot(), 0);
assert(Math.hypot(-2), 2);
assert(Math.hypot(3, 4), 5);
assert(Math.abs(Math.hypot(3, 4, 5) - 7.0710678118654755) <= 1e-15);
assert(Math.sumPrecise([1,Number.EPSILON/2,Number.MIN_VALUE]), 1.0000000000000002);
}

function test_number()
Expand Down
Loading
Loading