diff --git a/src/builtin.c b/src/builtin.c index b67f9c8d0e..71ca2e7786 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -90,11 +90,7 @@ static jv f_plus(jq_state *jq, jv input, jv a, jv b) { jv_free(b); return a; } else if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) { - jv r = jv_number(jv_number_value(a) + - jv_number_value(b)); - jv_free(a); - jv_free(b); - return r; + return jv_number_add(a, b); } else if (jv_get_kind(a) == JV_KIND_STRING && jv_get_kind(b) == JV_KIND_STRING) { return jv_string_concat(a, b); } else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) { diff --git a/src/jv.c b/src/jv.c index 3c38d42424..7d394215a0 100644 --- a/src/jv.c +++ b/src/jv.c @@ -230,28 +230,24 @@ static decContext* tsd_dec_ctx_get(pthread_key_t *key) { return ctx; } - decContext _ctx = { - 0, - DEC_MAX_EMAX, - DEC_MIN_EMAX, - DEC_ROUND_HALF_UP, - 0, /*no errors*/ - 0, /*status*/ - 0, /*no clamping*/ - }; + ctx = malloc(sizeof(decContext)); + if (!ctx) { + return ctx; + } + + // Initialize the context, clear any traps + decContextDefault(ctx, DEC_INIT_BASE); + ctx->traps = 0; + if (key == &dec_ctx_key) { - _ctx.digits = DEC_MAX_DIGITS; + ctx->digits = DEC_MAX_DIGITS; } else if (key == &dec_ctx_dbl_key) { - _ctx.digits = BIN64_DEC_PRECISION; + ctx->digits = BIN64_DEC_PRECISION; } - ctx = malloc(sizeof(decContext)); - if (ctx) { - *ctx = _ctx; - if (pthread_setspecific(*key, ctx) != 0) { - fprintf(stderr, "error: cannot store thread specific data"); - abort(); - } + if (pthread_setspecific(*key, ctx) != 0) { + fprintf(stderr, "error: cannot store thread specific data"); + abort(); } return ctx; } @@ -291,8 +287,9 @@ static decNumber* jvp_dec_number_ptr(jv j) { static jvp_literal_number* jvp_literal_number_alloc(unsigned literal_length) { - /* The number of units needed is ceil(DECNUMDIGITS/DECDPUN) */ - int units = ((literal_length+DECDPUN-1)/DECDPUN); + // The number of units needed is ceil(DECNUMDIGITS/DECDPUN) + // We already have one in the decNumber, so we subtract one from that + int units = ((literal_length+DECDPUN-1)/DECDPUN)-1; jvp_literal_number* n = jv_mem_alloc( sizeof(jvp_literal_number) @@ -314,6 +311,7 @@ static jv jvp_literal_number_new(const char * literal) { if (ctx->status & DEC_Conversion_syntax) { jv_mem_free(n); + decContextClearStatus(ctx, DEC_Conversion_syntax); return JV_INVALID; } @@ -484,6 +482,36 @@ int jvp_number_cmp(jv a, jv b) { } } +jv jv_number_add(jv a, jv b) { + if (jvp_number_is_literal(a) && jvp_number_is_literal(b)) { + decNumber *da = jvp_dec_number_ptr(a); + decNumber *db = jvp_dec_number_ptr(b); + + unsigned digits = MAX(da->digits+da->exponent, db->digits+db->exponent) - + MIN(da->exponent, db->exponent) + 1; + jvp_literal_number *nlit = jvp_literal_number_alloc(digits); + nlit->refcnt = JV_REFCNT_INIT; + nlit->literal_data = NULL; + nlit->num_double = NAN; + + decContext *ctx = DEC_CONTEXT(); + decNumberAdd(&nlit->num_decimal, da, db, ctx); + decNumberTrim(&nlit->num_decimal); + // TODO: Check context for error? + + jv_free(a); + jv_free(b); + + jv r = {JVP_FLAGS_NUMBER_LITERAL, 0, 0, JV_NUMBER_SIZE_INIT, {&nlit->refcnt}}; + return r; + } + + jv r = jv_number(jv_number_value(a) + jv_number_value(b)); + jv_free(a); + jv_free(b); + return r; +} + /* * Arrays (internal helpers) */ diff --git a/src/jv.h b/src/jv.h index 8c96f822f0..4a6d2d61ad 100644 --- a/src/jv.h +++ b/src/jv.h @@ -63,6 +63,7 @@ jv jv_number(double); jv jv_number_with_literal(const char*); double jv_number_value(jv); int jv_is_integer(jv); +jv jv_number_add(jv a, jv b); int jv_number_has_literal(jv n); const char* jv_number_get_literal(jv); diff --git a/src/parser.c b/src/parser.c index b6574e52c8..32270e1a01 100644 --- a/src/parser.c +++ b/src/parser.c @@ -369,10 +369,13 @@ static block constant_fold(block a, block b, int op) { double na = jv_number_value(jv_a); double nb = jv_number_value(jv_b); - int cmp = jv_cmp(jv_a, jv_b); + // Once we implement the rest of the arithmetic operations, all of these + // copy/frees can be optimized away, if we move the call to jv_cmp inside + // the switch cases. We'd just need to move the final frees into the default + int cmp = jv_cmp(jv_copy(jv_a), jv_copy(jv_b)); switch (op) { - case '+': res = jv_number(na + nb); break; + case '+': res = jv_number_add(jv_copy(jv_a), jv_copy(jv_b)); break; case '-': res = jv_number(na - nb); break; case '*': res = jv_number(na * nb); break; case '/': res = jv_number(na / nb); break; @@ -384,6 +387,8 @@ static block constant_fold(block a, block b, int op) { case GREATEREQ: res = (cmp >= 0 ? jv_true() : jv_false()); break; default: break; } + jv_free(jv_a); + jv_free(jv_b); } else if (op == '+' && block_const_kind(a) == JV_KIND_STRING) { res = jv_string_concat(block_const(a), block_const(b)); } else { diff --git a/src/parser.y b/src/parser.y index e223adab53..c68143b121 100644 --- a/src/parser.y +++ b/src/parser.y @@ -222,10 +222,13 @@ static block constant_fold(block a, block b, int op) { double na = jv_number_value(jv_a); double nb = jv_number_value(jv_b); - int cmp = jv_cmp(jv_a, jv_b); + // Once we implement the rest of the arithmetic operations, all of these + // copy/frees can be optimized away, if we move the call to jv_cmp inside + // the switch cases. We'd just need to move the final frees into the default + int cmp = jv_cmp(jv_copy(jv_a), jv_copy(jv_b)); switch (op) { - case '+': res = jv_number(na + nb); break; + case '+': res = jv_number_add(jv_copy(jv_a), jv_copy(jv_b)); break; case '-': res = jv_number(na - nb); break; case '*': res = jv_number(na * nb); break; case '/': res = jv_number(na / nb); break; @@ -237,6 +240,8 @@ static block constant_fold(block a, block b, int op) { case GREATEREQ: res = (cmp >= 0 ? jv_true() : jv_false()); break; default: break; } + jv_free(jv_a); + jv_free(jv_b); } else if (op == '+' && block_const_kind(a) == JV_KIND_STRING) { res = jv_string_concat(block_const(a), block_const(b)); } else { diff --git a/tests/jq.test b/tests/jq.test index adc5788962..bcc64d0714 100644 --- a/tests/jq.test +++ b/tests/jq.test @@ -1698,8 +1698,13 @@ map(. == 1) null false +# Applying addition to the value will not truncate the result to double -# Applying arithmetic to the value will truncate the result to double +. + 1 +418502930602131457 +418502930602131458 + +# Applying other arithmetic to the value will truncate the result to double . - 10 13911860366432393