From 05e59842c99f3db7e1607ce98d29b69ec2405d29 Mon Sep 17 00:00:00 2001 From: William Langford Date: Tue, 22 Oct 2019 15:21:00 -0400 Subject: [PATCH 1/5] Use decnum to add numbers --- src/builtin.c | 6 +----- src/jv.c | 33 +++++++++++++++++++++++++++++++-- src/jv.h | 1 + tests/jq.test | 7 ++++++- 4 files changed, 39 insertions(+), 8 deletions(-) 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..c4bdaef2da 100644 --- a/src/jv.c +++ b/src/jv.c @@ -291,8 +291,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 +315,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 +486,33 @@ 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, db->digits) + 1; + jvp_literal_number *nlit = jvp_literal_number_alloc(digits); + + nlit->refcnt = JV_REFCNT_INIT; + nlit->literal_data = NULL; + decContext *ctx = DEC_CONTEXT(); + nlit->num_double = NAN; + decNumberAdd(&nlit->num_decimal, da, db, ctx); + // 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/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 From 2e51cf5e54c3e124f19cc0355c82de54258234c6 Mon Sep 17 00:00:00 2001 From: William Langford Date: Tue, 22 Oct 2019 17:33:14 -0400 Subject: [PATCH 2/5] Clean up context creation --- src/jv.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/jv.c b/src/jv.c index c4bdaef2da..656346b247 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; } From 11de034c69547710ad5e2ee4b513395cabd7ccbe Mon Sep 17 00:00:00 2001 From: William Langford Date: Tue, 22 Oct 2019 17:57:09 -0400 Subject: [PATCH 3/5] fixup! Use decnum to add numbers --- src/jv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/jv.c b/src/jv.c index 656346b247..1d14322146 100644 --- a/src/jv.c +++ b/src/jv.c @@ -487,12 +487,13 @@ jv jv_number_add(jv a, jv b) { decNumber *da = jvp_dec_number_ptr(a); decNumber *db = jvp_dec_number_ptr(b); unsigned digits = MAX(da->digits, db->digits) + 1; - jvp_literal_number *nlit = jvp_literal_number_alloc(digits); + jvp_literal_number *nlit = jvp_literal_number_alloc(digits); nlit->refcnt = JV_REFCNT_INIT; nlit->literal_data = NULL; - decContext *ctx = DEC_CONTEXT(); nlit->num_double = NAN; + + decContext *ctx = DEC_CONTEXT(); decNumberAdd(&nlit->num_decimal, da, db, ctx); // TODO: Check context for error? From 08d6decf575a08b5fa53f13d2882943ed78a9bc9 Mon Sep 17 00:00:00 2001 From: William Langford Date: Tue, 22 Oct 2019 20:24:43 -0400 Subject: [PATCH 4/5] fixup! Use decnum to add numbers --- src/jv.c | 4 +++- src/parser.c | 2 +- src/parser.y | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/jv.c b/src/jv.c index 1d14322146..ca6f4d8fb2 100644 --- a/src/jv.c +++ b/src/jv.c @@ -486,8 +486,9 @@ 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, db->digits) + 1; + 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; @@ -495,6 +496,7 @@ jv jv_number_add(jv a, jv b) { decContext *ctx = DEC_CONTEXT(); decNumberAdd(&nlit->num_decimal, da, db, ctx); + decNumberReduce(&nlit->num_decimal, &nlit->num_decimal, ctx); // TODO: Check context for error? jv_free(a); diff --git a/src/parser.c b/src/parser.c index b6574e52c8..4cb6b12a0e 100644 --- a/src/parser.c +++ b/src/parser.c @@ -372,7 +372,7 @@ static block constant_fold(block a, block b, int op) { int cmp = jv_cmp(jv_a, 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; diff --git a/src/parser.y b/src/parser.y index e223adab53..411dc91529 100644 --- a/src/parser.y +++ b/src/parser.y @@ -225,7 +225,7 @@ static block constant_fold(block a, block b, int op) { int cmp = jv_cmp(jv_a, 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; From d812abbdb7575730ce6744ddc53216ab3bc38453 Mon Sep 17 00:00:00 2001 From: William Langford Date: Wed, 23 Oct 2019 09:17:18 -0400 Subject: [PATCH 5/5] fixup! Use decnum to add numbers --- src/jv.c | 2 +- src/parser.c | 7 ++++++- src/parser.y | 7 ++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/jv.c b/src/jv.c index ca6f4d8fb2..7d394215a0 100644 --- a/src/jv.c +++ b/src/jv.c @@ -496,7 +496,7 @@ jv jv_number_add(jv a, jv b) { decContext *ctx = DEC_CONTEXT(); decNumberAdd(&nlit->num_decimal, da, db, ctx); - decNumberReduce(&nlit->num_decimal, &nlit->num_decimal, ctx); + decNumberTrim(&nlit->num_decimal); // TODO: Check context for error? jv_free(a); diff --git a/src/parser.c b/src/parser.c index 4cb6b12a0e..32270e1a01 100644 --- a/src/parser.c +++ b/src/parser.c @@ -369,7 +369,10 @@ 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_add(jv_copy(jv_a), jv_copy(jv_b)); 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 411dc91529..c68143b121 100644 --- a/src/parser.y +++ b/src/parser.y @@ -222,7 +222,10 @@ 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_add(jv_copy(jv_a), jv_copy(jv_b)); 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 {