Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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], 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, 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