|
47 | 47 | #include "list.h"
|
48 | 48 | #include "quickjs.h"
|
49 | 49 | #include "libregexp.h"
|
50 |
| -#include "xsum.h" |
51 | 50 | #include "dtoa.h"
|
52 | 51 |
|
53 | 52 | #if defined(EMSCRIPTEN) || defined(_MSC_VER)
|
@@ -11998,7 +11997,7 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
|
11998 | 11997 | bool buf_allocated = false;
|
11999 | 11998 | JSValue val;
|
12000 | 11999 | JSATODTempMem atod_mem;
|
12001 |
| - |
| 12000 | + |
12002 | 12001 | /* optional separator between digits */
|
12003 | 12002 | sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
|
12004 | 12003 | has_legacy_octal = false;
|
@@ -12799,7 +12798,7 @@ static JSValue js_dtoa2(JSContext *ctx,
|
12799 | 12798 | JSValue res;
|
12800 | 12799 | JSDTOATempMem dtoa_mem;
|
12801 | 12800 | len_max = js_dtoa_max_len(d, radix, n_digits, flags);
|
12802 |
| - |
| 12801 | + |
12803 | 12802 | /* longer buffer may be used if radix != 10 */
|
12804 | 12803 | if (len_max > sizeof(static_buf) - 1) {
|
12805 | 12804 | tmp_buf = js_malloc(ctx, len_max + 1);
|
@@ -44667,93 +44666,249 @@ static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,
|
44667 | 44666 | return js_int32(r);
|
44668 | 44667 | }
|
44669 | 44668 |
|
44670 |
| -typedef enum SumPreciseStateEnum { |
| 44669 | +/* we add one extra limb to avoid having to test for overflows during the sum */ |
| 44670 | +#define SUM_PRECISE_ACC_LEN 34 |
| 44671 | + |
| 44672 | +typedef enum { |
44671 | 44673 | SUM_PRECISE_STATE_MINUS_ZERO,
|
44672 |
| - SUM_PRECISE_STATE_NOT_A_NUMBER, |
44673 |
| - SUM_PRECISE_STATE_MINUS_INFINITY, |
44674 |
| - SUM_PRECISE_STATE_PLUS_INFINITY, |
44675 | 44674 | SUM_PRECISE_STATE_FINITE,
|
| 44675 | + SUM_PRECISE_STATE_INFINITY, |
| 44676 | + SUM_PRECISE_STATE_MINUS_INFINITY, /* must be after SUM_PRECISE_STATE_INFINITY */ |
| 44677 | + SUM_PRECISE_STATE_NAN, /* must be after SUM_PRECISE_STATE_MINUS_INFINITY */ |
44676 | 44678 | } SumPreciseStateEnum;
|
44677 | 44679 |
|
| 44680 | +typedef struct { |
| 44681 | + uint64_t acc[SUM_PRECISE_ACC_LEN]; |
| 44682 | + int n_limbs; /* acc is not necessarily normalized */ |
| 44683 | + SumPreciseStateEnum state; |
| 44684 | +} SumPreciseState; |
| 44685 | + |
| 44686 | +static void sum_precise_init(SumPreciseState *s) |
| 44687 | +{ |
| 44688 | + s->state = SUM_PRECISE_STATE_MINUS_ZERO; |
| 44689 | + s->acc[0] = 0; |
| 44690 | + s->n_limbs = 1; |
| 44691 | +} |
| 44692 | + |
| 44693 | +#define ADDC64(res, carry_out, op1, op2, carry_in) \ |
| 44694 | +do { \ |
| 44695 | + uint64_t __v, __a, __k, __k1; \ |
| 44696 | + __v = (op1); \ |
| 44697 | + __a = __v + (op2); \ |
| 44698 | + __k1 = __a < __v; \ |
| 44699 | + __k = (carry_in); \ |
| 44700 | + __a = __a + __k; \ |
| 44701 | + carry_out = (__a < __k) | __k1; \ |
| 44702 | + res = __a; \ |
| 44703 | +} while (0) |
| 44704 | + |
| 44705 | +static void sum_precise_add(SumPreciseState *s, double d) |
| 44706 | +{ |
| 44707 | + uint64_t a, m, a0, carry, acc_sign, a_sign; |
| 44708 | + int sgn, e, p, n, i; |
| 44709 | + unsigned shift; |
| 44710 | + |
| 44711 | + a = float64_as_uint64(d); |
| 44712 | + sgn = a >> 63; |
| 44713 | + e = (a >> 52) & ((1 << 11) - 1); |
| 44714 | + m = a & (((uint64_t)1 << 52) - 1); |
| 44715 | + if (unlikely(e == 2047)) { |
| 44716 | + if (m == 0) { |
| 44717 | + /* +/- infinity */ |
| 44718 | + if (s->state == SUM_PRECISE_STATE_NAN || |
| 44719 | + (s->state == SUM_PRECISE_STATE_MINUS_INFINITY && !sgn) || |
| 44720 | + (s->state == SUM_PRECISE_STATE_INFINITY && sgn)) { |
| 44721 | + s->state = SUM_PRECISE_STATE_NAN; |
| 44722 | + } else { |
| 44723 | + s->state = SUM_PRECISE_STATE_INFINITY + sgn; |
| 44724 | + } |
| 44725 | + } else { |
| 44726 | + /* NaN */ |
| 44727 | + s->state = SUM_PRECISE_STATE_NAN; |
| 44728 | + } |
| 44729 | + } else if (e == 0) { |
| 44730 | + if (likely(m == 0)) { |
| 44731 | + /* zero */ |
| 44732 | + if (s->state == SUM_PRECISE_STATE_MINUS_ZERO && !sgn) |
| 44733 | + s->state = SUM_PRECISE_STATE_FINITE; |
| 44734 | + } else { |
| 44735 | + /* subnormal */ |
| 44736 | + p = 0; |
| 44737 | + shift = 0; |
| 44738 | + goto add; |
| 44739 | + } |
| 44740 | + } else { |
| 44741 | + m |= (uint64_t)1 << 52; |
| 44742 | + shift = e - 1; |
| 44743 | + p = shift / 64; |
| 44744 | + /* 'p' is the position of a0 in acc */ |
| 44745 | + shift %= 64; |
| 44746 | + add: |
| 44747 | + if (s->state >= SUM_PRECISE_STATE_INFINITY) |
| 44748 | + return; |
| 44749 | + s->state = SUM_PRECISE_STATE_FINITE; |
| 44750 | + n = s->n_limbs; |
| 44751 | + |
| 44752 | + acc_sign = (int64_t)s->acc[n - 1] >> 63; |
| 44753 | + |
| 44754 | + /* sign extend acc */ |
| 44755 | + for(i = n; i <= p; i++) |
| 44756 | + s->acc[i] = acc_sign; |
| 44757 | + |
| 44758 | + carry = sgn; |
| 44759 | + a_sign = -sgn; |
| 44760 | + a0 = m << shift; |
| 44761 | + ADDC64(s->acc[p], carry, s->acc[p], a0 ^ a_sign, carry); |
| 44762 | + if (shift >= 12) { |
| 44763 | + p++; |
| 44764 | + if (p >= n) |
| 44765 | + s->acc[p] = acc_sign; |
| 44766 | + a0 = m >> (64 - shift); |
| 44767 | + ADDC64(s->acc[p], carry, s->acc[p], a0 ^ a_sign, carry); |
| 44768 | + } |
| 44769 | + p++; |
| 44770 | + if (p >= n) { |
| 44771 | + n = p; |
| 44772 | + } else { |
| 44773 | + /* carry */ |
| 44774 | + for(i = p; i < n; i++) { |
| 44775 | + /* if 'a' positive: stop condition: carry = 0. |
| 44776 | + if 'a' negative: stop condition: carry = 1. */ |
| 44777 | + if (carry == sgn) |
| 44778 | + goto done; |
| 44779 | + ADDC64(s->acc[i], carry, s->acc[i], a_sign, carry); |
| 44780 | + } |
| 44781 | + } |
| 44782 | + |
| 44783 | + /* extend the accumulator if needed */ |
| 44784 | + a0 = carry + acc_sign + a_sign; |
| 44785 | + /* -1 <= a0 <= 1 (if both acc and a are negative, carry is set) */ |
| 44786 | + if (a0 != ((int64_t)s->acc[n - 1] >> 63)) { |
| 44787 | + s->acc[n++] = a0; |
| 44788 | + } |
| 44789 | + done: |
| 44790 | + s->n_limbs = n; |
| 44791 | + } |
| 44792 | +} |
| 44793 | + |
| 44794 | +static double sum_precise_get_result(SumPreciseState *s) |
| 44795 | +{ |
| 44796 | + int n, shift, e, p, is_neg, i; |
| 44797 | + uint64_t m, addend, carry; |
| 44798 | + |
| 44799 | + if (s->state != SUM_PRECISE_STATE_FINITE) { |
| 44800 | + switch(s->state) { |
| 44801 | + default: |
| 44802 | + case SUM_PRECISE_STATE_MINUS_ZERO: |
| 44803 | + return -0.0; |
| 44804 | + case SUM_PRECISE_STATE_INFINITY: |
| 44805 | + return INFINITY; |
| 44806 | + case SUM_PRECISE_STATE_MINUS_INFINITY: |
| 44807 | + return -INFINITY; |
| 44808 | + case SUM_PRECISE_STATE_NAN: |
| 44809 | + return NAN; |
| 44810 | + } |
| 44811 | + } |
| 44812 | + |
| 44813 | + /* extract the sign and absolute value */ |
| 44814 | + n = s->n_limbs; |
| 44815 | + is_neg = s->acc[n - 1] >> 63; |
| 44816 | + if (is_neg) { |
| 44817 | + /* acc = -acc */ |
| 44818 | + carry = 1; |
| 44819 | + for(i = 0; i < n; i++) { |
| 44820 | + ADDC64(s->acc[i], carry, ~s->acc[i], 0, carry); |
| 44821 | + } |
| 44822 | + } |
| 44823 | + /* normalize */ |
| 44824 | + while (n > 0 && s->acc[n - 1] == 0) |
| 44825 | + n--; |
| 44826 | + /* zero result. The spec tells it is always positive in the finite case */ |
| 44827 | + if (n == 0) |
| 44828 | + return 0.0; |
| 44829 | + /* subnormal case */ |
| 44830 | + if (n == 1 && s->acc[0] < ((uint64_t)1 << 52)) |
| 44831 | + return uint64_as_float64(((uint64_t)is_neg << 63) | s->acc[0]); |
| 44832 | + /* normal case */ |
| 44833 | + e = n * 64; |
| 44834 | + p = n - 1; |
| 44835 | + m = s->acc[p]; |
| 44836 | + shift = clz64(m); |
| 44837 | + e = e - shift - 52; |
| 44838 | + if (shift != 0) { |
| 44839 | + m <<= shift; |
| 44840 | + if (p > 0) { |
| 44841 | + int shift1; |
| 44842 | + uint64_t nz; |
| 44843 | + p--; |
| 44844 | + shift1 = 64 - shift; |
| 44845 | + nz = s->acc[p] & (((uint64_t)1 << shift1) - 1); |
| 44846 | + m = m | (s->acc[p] >> shift1) | (nz != 0); |
| 44847 | + } |
| 44848 | + } |
| 44849 | + if ((m & ((1 << 10) - 1)) == 0) { |
| 44850 | + /* see if the LSB part is non zero for the final rounding */ |
| 44851 | + while (p > 0) { |
| 44852 | + p--; |
| 44853 | + if (s->acc[p] != 0) { |
| 44854 | + m |= 1; |
| 44855 | + break; |
| 44856 | + } |
| 44857 | + } |
| 44858 | + } |
| 44859 | + /* rounding to nearest with ties to even */ |
| 44860 | + addend = (1 << 10) - 1 + ((m >> 11) & 1); |
| 44861 | + m = (m + addend) >> 11; |
| 44862 | + /* handle overflow in the rounding */ |
| 44863 | + if (m == 0) |
| 44864 | + e++; |
| 44865 | + if (unlikely(e >= 2047)) { |
| 44866 | + /* infinity */ |
| 44867 | + return uint64_as_float64(((uint64_t)is_neg << 63) | ((uint64_t)2047 << 52)); |
| 44868 | + } else { |
| 44869 | + m &= (((uint64_t)1 << 52) - 1); |
| 44870 | + return uint64_as_float64(((uint64_t)is_neg << 63) | ((uint64_t)e << 52) | m); |
| 44871 | + } |
| 44872 | +} |
| 44873 | + |
44678 | 44874 | static JSValue js_math_sumPrecise(JSContext *ctx, JSValueConst this_val,
|
44679 | 44875 | int argc, JSValueConst *argv)
|
44680 | 44876 | {
|
44681 | 44877 | JSValue iter, next, item, ret;
|
| 44878 | + uint32_t tag; |
44682 | 44879 | int done;
|
44683 | 44880 | double d;
|
44684 |
| - xsum_small_accumulator acc; |
44685 |
| - SumPreciseStateEnum state; |
| 44881 | + SumPreciseState s_s, *s = &s_s; |
44686 | 44882 |
|
44687 |
| - iter = JS_GetIterator(ctx, argv[0], /*async*/false); |
| 44883 | + iter = JS_GetIterator(ctx, argv[0], /*is_async*/false); |
44688 | 44884 | if (JS_IsException(iter))
|
44689 | 44885 | return JS_EXCEPTION;
|
44690 | 44886 | ret = JS_EXCEPTION;
|
44691 | 44887 | next = JS_GetProperty(ctx, iter, JS_ATOM_next);
|
44692 | 44888 | if (JS_IsException(next))
|
44693 | 44889 | goto fail;
|
44694 |
| - xsum_small_init(&acc); |
44695 |
| - state = SUM_PRECISE_STATE_MINUS_ZERO; |
| 44890 | + sum_precise_init(s); |
44696 | 44891 | for (;;) {
|
44697 | 44892 | item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
|
44698 | 44893 | if (JS_IsException(item))
|
44699 | 44894 | goto fail;
|
44700 | 44895 | if (done)
|
44701 |
| - break; // item == JS_UNDEFINED |
44702 |
| - switch (JS_VALUE_GET_TAG(item)) { |
44703 |
| - default: |
| 44896 | + break; |
| 44897 | + tag = JS_VALUE_GET_TAG(item); |
| 44898 | + if (JS_TAG_IS_FLOAT64(tag)) { |
| 44899 | + d = JS_VALUE_GET_FLOAT64(item); |
| 44900 | + } else if (tag == JS_TAG_INT) { |
| 44901 | + d = JS_VALUE_GET_INT(item); |
| 44902 | + } else { |
44704 | 44903 | JS_FreeValue(ctx, item);
|
44705 | 44904 | JS_ThrowTypeError(ctx, "not a number");
|
| 44905 | + JS_IteratorClose(ctx, iter, /*is_exception_pending*/true); |
44706 | 44906 | goto fail;
|
44707 |
| - case JS_TAG_INT: |
44708 |
| - d = JS_VALUE_GET_INT(item); |
44709 |
| - break; |
44710 |
| - case JS_TAG_FLOAT64: |
44711 |
| - d = JS_VALUE_GET_FLOAT64(item); |
44712 |
| - break; |
44713 |
| - } |
44714 |
| - |
44715 |
| - if (state != SUM_PRECISE_STATE_NOT_A_NUMBER) { |
44716 |
| - if (isnan(d)) |
44717 |
| - state = SUM_PRECISE_STATE_NOT_A_NUMBER; |
44718 |
| - else if (!isfinite(d) && d > 0.0) |
44719 |
| - if (state == SUM_PRECISE_STATE_MINUS_INFINITY) |
44720 |
| - state = SUM_PRECISE_STATE_NOT_A_NUMBER; |
44721 |
| - else |
44722 |
| - state = SUM_PRECISE_STATE_PLUS_INFINITY; |
44723 |
| - else if (!isfinite(d) && d < 0.0) |
44724 |
| - if (state == SUM_PRECISE_STATE_PLUS_INFINITY) |
44725 |
| - state = SUM_PRECISE_STATE_NOT_A_NUMBER; |
44726 |
| - else |
44727 |
| - state = SUM_PRECISE_STATE_MINUS_INFINITY; |
44728 |
| - else if (!(d == 0.0 && signbit(d)) && (state == SUM_PRECISE_STATE_MINUS_ZERO || state == SUM_PRECISE_STATE_FINITE)) { |
44729 |
| - state = SUM_PRECISE_STATE_FINITE; |
44730 |
| - xsum_small_add1(&acc, d); |
44731 |
| - } |
44732 | 44907 | }
|
| 44908 | + sum_precise_add(s, d); |
44733 | 44909 | }
|
44734 |
| - |
44735 |
| - switch (state) { |
44736 |
| - case SUM_PRECISE_STATE_NOT_A_NUMBER: |
44737 |
| - d = NAN; |
44738 |
| - break; |
44739 |
| - case SUM_PRECISE_STATE_MINUS_INFINITY: |
44740 |
| - d = -INFINITY; |
44741 |
| - break; |
44742 |
| - case SUM_PRECISE_STATE_PLUS_INFINITY: |
44743 |
| - d = INFINITY; |
44744 |
| - break; |
44745 |
| - case SUM_PRECISE_STATE_MINUS_ZERO: |
44746 |
| - d = -0.0; |
44747 |
| - break; |
44748 |
| - case SUM_PRECISE_STATE_FINITE: |
44749 |
| - d = xsum_small_round(&acc); |
44750 |
| - break; |
44751 |
| - default: |
44752 |
| - abort(); |
44753 |
| - } |
44754 |
| - ret = js_float64(d); |
| 44910 | + ret = js_float64(sum_precise_get_result(s)); |
44755 | 44911 | fail:
|
44756 |
| - JS_IteratorClose(ctx, iter, JS_IsException(ret)); |
44757 | 44912 | JS_FreeValue(ctx, iter);
|
44758 | 44913 | JS_FreeValue(ctx, next);
|
44759 | 44914 | return ret;
|
|
0 commit comments