Skip to content

Commit bc753c6

Browse files
author
Fabrice Bellard
committed
added Math.sumPrecise()
1 parent 456e016 commit bc753c6

File tree

4 files changed

+260
-2
lines changed

4 files changed

+260
-2
lines changed

TODO

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,5 @@ Optimization ideas:
6262
Test262o: 0/11262 errors, 463 excluded
6363
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
6464

65-
Result: 48/81914 errors, 1631 excluded, 5486 skipped
65+
Result: 48/81934 errors, 1631 excluded, 5476 skipped
6666
Test262 commit: e7e136756cd67c1ffcf7c09d03aeb8ad5a6cec0c

quickjs.c

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45458,6 +45458,262 @@ static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,
4545845458
return JS_NewInt32(ctx, r);
4545945459
}
4546045460

45461+
/* we add one extra limb to avoid having to test for overflows during the sum */
45462+
#define SUM_PRECISE_ACC_LEN 34
45463+
45464+
typedef enum {
45465+
SUM_PRECISE_STATE_MINUS_ZERO,
45466+
SUM_PRECISE_STATE_FINITE,
45467+
SUM_PRECISE_STATE_INFINITY,
45468+
SUM_PRECISE_STATE_MINUS_INFINITY, /* must be after SUM_PRECISE_STATE_INFINITY */
45469+
SUM_PRECISE_STATE_NAN, /* must be after SUM_PRECISE_STATE_MINUS_INFINITY */
45470+
} SumPreciseStateEnum;
45471+
45472+
typedef struct {
45473+
uint64_t acc[SUM_PRECISE_ACC_LEN];
45474+
int n_limbs; /* acc is not necessarily normalized */
45475+
SumPreciseStateEnum state;
45476+
} SumPreciseState;
45477+
45478+
static void sum_precise_init(SumPreciseState *s)
45479+
{
45480+
s->state = SUM_PRECISE_STATE_MINUS_ZERO;
45481+
s->acc[0] = 0;
45482+
s->n_limbs = 1;
45483+
}
45484+
45485+
#define ADDC64(res, carry_out, op1, op2, carry_in) \
45486+
do { \
45487+
uint64_t __v, __a, __k, __k1; \
45488+
__v = (op1); \
45489+
__a = __v + (op2); \
45490+
__k1 = __a < __v; \
45491+
__k = (carry_in); \
45492+
__a = __a + __k; \
45493+
carry_out = (__a < __k) | __k1; \
45494+
res = __a; \
45495+
} while (0)
45496+
45497+
static void sum_precise_add(SumPreciseState *s, double d)
45498+
{
45499+
uint64_t a, m, a0, carry, acc_sign, a_sign;
45500+
int sgn, e, p, n, i;
45501+
unsigned shift;
45502+
45503+
a = float64_as_uint64(d);
45504+
sgn = a >> 63;
45505+
e = (a >> 52) & ((1 << 11) - 1);
45506+
m = a & (((uint64_t)1 << 52) - 1);
45507+
if (unlikely(e == 2047)) {
45508+
if (m == 0) {
45509+
/* +/- infinity */
45510+
if (s->state == SUM_PRECISE_STATE_NAN ||
45511+
(s->state == SUM_PRECISE_STATE_MINUS_INFINITY && !sgn) ||
45512+
(s->state == SUM_PRECISE_STATE_INFINITY && sgn)) {
45513+
s->state = SUM_PRECISE_STATE_NAN;
45514+
} else {
45515+
s->state = SUM_PRECISE_STATE_INFINITY + sgn;
45516+
}
45517+
} else {
45518+
/* NaN */
45519+
s->state = SUM_PRECISE_STATE_NAN;
45520+
}
45521+
} else if (e == 0) {
45522+
if (likely(m == 0)) {
45523+
/* zero */
45524+
if (s->state == SUM_PRECISE_STATE_MINUS_ZERO && !sgn)
45525+
s->state = SUM_PRECISE_STATE_FINITE;
45526+
} else {
45527+
/* subnormal */
45528+
p = 0;
45529+
shift = 0;
45530+
goto add;
45531+
}
45532+
} else {
45533+
m |= (uint64_t)1 << 52;
45534+
shift = e - 1;
45535+
p = shift / 64;
45536+
/* 'p' is the position of a0 in acc */
45537+
shift %= 64;
45538+
add:
45539+
if (s->state >= SUM_PRECISE_STATE_INFINITY)
45540+
return;
45541+
s->state = SUM_PRECISE_STATE_FINITE;
45542+
n = s->n_limbs;
45543+
45544+
acc_sign = (int64_t)s->acc[n - 1] >> 63;
45545+
45546+
/* sign extend acc */
45547+
for(i = n; i <= p; i++)
45548+
s->acc[i] = acc_sign;
45549+
45550+
carry = sgn;
45551+
a_sign = -sgn;
45552+
a0 = m << shift;
45553+
ADDC64(s->acc[p], carry, s->acc[p], a0 ^ a_sign, carry);
45554+
if (shift >= 12) {
45555+
p++;
45556+
if (p >= n)
45557+
s->acc[p] = acc_sign;
45558+
a0 = m >> (64 - shift);
45559+
ADDC64(s->acc[p], carry, s->acc[p], a0 ^ a_sign, carry);
45560+
}
45561+
p++;
45562+
if (p >= n) {
45563+
n = p;
45564+
} else {
45565+
/* carry */
45566+
for(i = p; i < n; i++) {
45567+
/* if 'a' positive: stop condition: carry = 0.
45568+
if 'a' negative: stop condition: carry = 1. */
45569+
if (carry == sgn)
45570+
goto done;
45571+
ADDC64(s->acc[i], carry, s->acc[i], a_sign, carry);
45572+
}
45573+
}
45574+
45575+
/* extend the accumulator if needed */
45576+
a0 = carry + acc_sign + a_sign;
45577+
/* -1 <= a0 <= 1 (if both acc and a are negative, carry is set) */
45578+
if (a0 != ((int64_t)s->acc[n - 1] >> 63)) {
45579+
s->acc[n++] = a0;
45580+
}
45581+
done:
45582+
s->n_limbs = n;
45583+
}
45584+
}
45585+
45586+
static double sum_precise_get_result(SumPreciseState *s)
45587+
{
45588+
int n, shift, e, p, is_neg, i;
45589+
uint64_t m, addend, carry;
45590+
45591+
if (s->state != SUM_PRECISE_STATE_FINITE) {
45592+
switch(s->state) {
45593+
default:
45594+
case SUM_PRECISE_STATE_MINUS_ZERO:
45595+
return -0.0;
45596+
case SUM_PRECISE_STATE_INFINITY:
45597+
return INFINITY;
45598+
case SUM_PRECISE_STATE_MINUS_INFINITY:
45599+
return -INFINITY;
45600+
case SUM_PRECISE_STATE_NAN:
45601+
return NAN;
45602+
}
45603+
}
45604+
45605+
/* extract the sign and absolute value */
45606+
n = s->n_limbs;
45607+
is_neg = s->acc[n - 1] >> 63;
45608+
if (is_neg) {
45609+
/* acc = -acc */
45610+
carry = 1;
45611+
for(i = 0; i < n; i++) {
45612+
ADDC64(s->acc[i], carry, ~s->acc[i], 0, carry);
45613+
}
45614+
}
45615+
/* normalize */
45616+
while (n > 0 && s->acc[n - 1] == 0)
45617+
n--;
45618+
#if 0
45619+
{
45620+
printf("res=");
45621+
for(i = n - 1; i >= 0; i--)
45622+
printf(" %016lx", s->acc[i]);
45623+
printf("\n");
45624+
}
45625+
#endif
45626+
/* zero result. The spec tells it is always positive in the finite case */
45627+
if (n == 0)
45628+
return 0.0;
45629+
/* subnormal case */
45630+
if (n == 1 && s->acc[0] < ((uint64_t)1 << 52))
45631+
return uint64_as_float64(((uint64_t)is_neg << 63) | s->acc[0]);
45632+
/* normal case */
45633+
e = n * 64;
45634+
p = n - 1;
45635+
m = s->acc[p];
45636+
shift = clz64(m);
45637+
e = e - shift - 52;
45638+
if (shift != 0) {
45639+
m <<= shift;
45640+
if (p > 0) {
45641+
int shift1;
45642+
uint64_t nz;
45643+
p--;
45644+
shift1 = 64 - shift;
45645+
nz = s->acc[p] & (((uint64_t)1 << shift1) - 1);
45646+
m = m | (s->acc[p] >> shift1) | (nz != 0);
45647+
}
45648+
}
45649+
if ((m & ((1 << 10) - 1)) == 0) {
45650+
/* see if the LSB part is non zero for the final rounding */
45651+
while (p > 0) {
45652+
p--;
45653+
if (s->acc[p] != 0) {
45654+
m |= 1;
45655+
break;
45656+
}
45657+
}
45658+
}
45659+
/* rounding to nearest with ties to even */
45660+
addend = (1 << 10) - 1 + ((m >> 11) & 1);
45661+
m = (m + addend) >> 11;
45662+
/* handle overflow in the rounding */
45663+
if (m == 0)
45664+
e++;
45665+
if (unlikely(e >= 2047)) {
45666+
/* infinity */
45667+
return uint64_as_float64(((uint64_t)is_neg << 63) | ((uint64_t)2047 << 52));
45668+
} else {
45669+
m &= (((uint64_t)1 << 52) - 1);
45670+
return uint64_as_float64(((uint64_t)is_neg << 63) | ((uint64_t)e << 52) | m);
45671+
}
45672+
}
45673+
45674+
static JSValue js_math_sumPrecise(JSContext *ctx, JSValueConst this_val,
45675+
int argc, JSValueConst *argv)
45676+
{
45677+
JSValue iter, next, item, ret;
45678+
uint32_t tag;
45679+
int done;
45680+
double d;
45681+
SumPreciseState s_s, *s = &s_s;
45682+
45683+
iter = JS_GetIterator(ctx, argv[0], FALSE);
45684+
if (JS_IsException(iter))
45685+
return JS_EXCEPTION;
45686+
ret = JS_EXCEPTION;
45687+
next = JS_GetProperty(ctx, iter, JS_ATOM_next);
45688+
if (JS_IsException(next))
45689+
goto fail;
45690+
sum_precise_init(s);
45691+
for (;;) {
45692+
item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
45693+
if (JS_IsException(item))
45694+
goto fail;
45695+
if (done)
45696+
break;
45697+
tag = JS_VALUE_GET_TAG(item);
45698+
if (JS_TAG_IS_FLOAT64(tag)) {
45699+
d = JS_VALUE_GET_FLOAT64(item);
45700+
} else if (tag == JS_TAG_INT) {
45701+
d = JS_VALUE_GET_INT(item);
45702+
} else {
45703+
JS_FreeValue(ctx, item);
45704+
JS_ThrowTypeError(ctx, "not a number");
45705+
JS_IteratorClose(ctx, iter, TRUE);
45706+
goto fail;
45707+
}
45708+
sum_precise_add(s, d);
45709+
}
45710+
ret = __JS_NewFloat64(ctx, sum_precise_get_result(s));
45711+
fail:
45712+
JS_FreeValue(ctx, iter);
45713+
JS_FreeValue(ctx, next);
45714+
return ret;
45715+
}
45716+
4546145717
/* xorshift* random number generator by Marsaglia */
4546245718
static uint64_t xorshift64star(uint64_t *pstate)
4546345719
{
@@ -45531,6 +45787,7 @@ static const JSCFunctionListEntry js_math_funcs[] = {
4553145787
JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),
4553245788
JS_CFUNC_DEF("imul", 2, js_math_imul ),
4553345789
JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),
45790+
JS_CFUNC_DEF("sumPrecise", 1, js_math_sumPrecise ),
4553445791
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ),
4553545792
JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ),
4553645793
JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ),

test262.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ legacy-regexp=skip
149149
let
150150
logical-assignment-operators
151151
Map
152-
Math.sumPrecise=skip
152+
Math.sumPrecise
153153
new.target
154154
numeric-separator-literal
155155
object-rest

tests/test_builtin.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ function test_math()
357357
assert(Math.hypot(-2), 2);
358358
assert(Math.hypot(3, 4), 5);
359359
assert(Math.abs(Math.hypot(3, 4, 5) - 7.0710678118654755) <= 1e-15);
360+
assert(Math.sumPrecise([1,Number.EPSILON/2,Number.MIN_VALUE]), 1.0000000000000002);
360361
}
361362

362363
function test_number()

0 commit comments

Comments
 (0)