Skip to content

Commit

Permalink
Convert decnum to binary64 (double) instead of decimal64
Browse files Browse the repository at this point in the history
This is what the JSON spec suggests and will also be less confusing compared to other jq implementations and langauges.

Related to #2939
  • Loading branch information
wader committed Nov 26, 2023
1 parent 88f01a7 commit b8249e6
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 16 deletions.
26 changes: 10 additions & 16 deletions src/jv.c
Expand Up @@ -213,7 +213,7 @@ enum {
#define JVP_FLAGS_NUMBER_LITERAL JVP_MAKE_FLAGS(JV_KIND_NUMBER, JVP_MAKE_PFLAGS(JVP_NUMBER_DECIMAL, 1))

// the decimal precision of binary double
#define DEC_NUBMER_DOUBLE_PRECISION (16)
#define DEC_NUBMER_DOUBLE_PRECISION (17)
#define DEC_NUMBER_STRING_GUARD (14)
#define DEC_NUBMER_DOUBLE_EXTRA_UNITS ((DEC_NUBMER_DOUBLE_PRECISION - DECNUMDIGITS + DECDPUN - 1)/DECDPUN)

Expand Down Expand Up @@ -489,30 +489,22 @@ pthread_getspecific(pthread_key_t key)
#endif

static pthread_key_t dec_ctx_key;
static pthread_key_t dec_ctx_dbl_key;
static pthread_once_t dec_ctx_once = PTHREAD_ONCE_INIT;

#define DEC_CONTEXT() tsd_dec_ctx_get(&dec_ctx_key)
#define DEC_CONTEXT_TO_DOUBLE() tsd_dec_ctx_get(&dec_ctx_dbl_key)

// atexit finalizer to clean up the tsd dec contexts if main() exits
// without having called pthread_exit()
void jv_tsd_dec_ctx_fini() {
jv_mem_free(pthread_getspecific(dec_ctx_key));
jv_mem_free(pthread_getspecific(dec_ctx_dbl_key));
pthread_setspecific(dec_ctx_key, NULL);
pthread_setspecific(dec_ctx_dbl_key, NULL);
}

void jv_tsd_dec_ctx_init() {
if (pthread_key_create(&dec_ctx_key, jv_mem_free) != 0) {
fprintf(stderr, "error: cannot create thread specific key");
abort();
}
if (pthread_key_create(&dec_ctx_dbl_key, jv_mem_free) != 0) {
fprintf(stderr, "error: cannot create thread specific key");
abort();
}
atexit(jv_tsd_dec_ctx_fini);
}

Expand All @@ -533,12 +525,6 @@ static decContext* tsd_dec_ctx_get(pthread_key_t *key) {
INT32_MAX - (DECDPUN - 1) - (ctx->emax - ctx->emin - 1));
ctx->traps = 0; /*no errors*/
}
else if (key == &dec_ctx_dbl_key)
{
decContextDefault(ctx, DEC_INIT_DECIMAL64);
// just to make sure we got this right
assert(ctx->digits <= DEC_NUBMER_DOUBLE_PRECISION);
}
if (pthread_setspecific(*key, ctx) != 0) {
fprintf(stderr, "error: cannot store thread specific data");
abort();
Expand Down Expand Up @@ -610,14 +596,22 @@ static jv jvp_literal_number_new(const char * literal) {

static double jvp_literal_number_to_double(jv j) {
assert(JVP_HAS_FLAGS(j, JVP_FLAGS_NUMBER_LITERAL));
decContext dblCtx;

dblCtx.digits = DEC_NUBMER_DOUBLE_PRECISION;
dblCtx.emax = 384;
dblCtx.emin = -383;
dblCtx.round = DEC_ROUND_HALF_EVEN;
dblCtx.traps = 0;
dblCtx.clamp = 1;

decNumber *p_dec_number = jvp_dec_number_ptr(j);
decNumberDoublePrecision dec_double;
char literal[DEC_NUBMER_DOUBLE_PRECISION + DEC_NUMBER_STRING_GUARD + 1];

// reduce the number to the shortest possible form
// that fits into the 64 bit floating point representation
decNumberReduce(&dec_double.number, p_dec_number, DEC_CONTEXT_TO_DOUBLE());
decNumberReduce(&dec_double.number, p_dec_number, &dblCtx);

decNumberToString(&dec_double.number, literal);

Expand Down
9 changes: 9 additions & 0 deletions tests/jq.test
Expand Up @@ -1867,6 +1867,15 @@ false
1
1

# decnum to double conversion
.[] as $n | $n+0 | [., tostring, . == $n]
[-9007199254740993, -9007199254740992, 9007199254740992, 9007199254740993, 13911860366432393]
[-9007199254740992,"-9007199254740992",true]
[-9007199254740992,"-9007199254740992",true]
[9007199254740992,"9007199254740992",true]
[9007199254740992,"9007199254740992",true]
[13911860366432392,"13911860366432392",true]

# abs, fabs, length
abs
"abc"
Expand Down

0 comments on commit b8249e6

Please sign in to comment.