From e04883d448df6e622535020449903e2f2a0f32c9 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Tue, 26 Jul 2011 17:11:41 +0200 Subject: Update integer and floating point number comparisons For floating point values which are greater than 9007199254740990.0 or smaller than -9007199254740990.0, the floating point numbers are now converted to integers during comparison with an integer. This makes number comparisons transitive for large floating point numbers. --- erts/emulator/beam/big.c | 57 +++++++++++++++++++++++++++++++++++++++++++ erts/emulator/beam/big.h | 1 + erts/emulator/beam/erl_vm.h | 2 +- erts/emulator/beam/utils.c | 59 ++++++++++++++++++++++++++++++++++++--------- 4 files changed, 107 insertions(+), 12 deletions(-) (limited to 'erts') diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index d18de9ae5d..51e5d2d305 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1584,6 +1584,63 @@ big_to_double(Wterm x, double* resp) return 0; } +Eterm +double_to_big(double x, Eterm *heap) +{ + int is_negative; + int ds; + ErtsDigit* xp; + Eterm tmp_res; + int i; + size_t sz; + Eterm* hp; + double dbase; + + if ((x < (double) (MAX_SMALL + 1)) && (x > (double) (MIN_SMALL - 1))) { + Sint xi = x; + return make_small(xi); + } + + if (x >= 0) { + is_negative = 0; + } else { + is_negative = 1; + x = -x; + } + + /* Unscale & (calculate exponent) */ + ds = 0; + dbase = ((double) (D_MASK) + 1); + while (x >= 1.0) { + x /= dbase; /* "shift" right */ + ds++; + } + sz = BIG_NEED_SIZE(ds); /* number of words including arity */ + + hp = heap; + tmp_res = make_big(hp); + xp = (ErtsDigit*) (hp + 1); + + for (i = ds - 1; i >= 0; i--) { + ErtsDigit d; + + x *= dbase; /* "shift" left */ + d = x; /* trunc */ + xp[i] = d; /* store digit */ + x -= d; /* remove integer part */ + } + while ((ds & (BIG_DIGITS_PER_WORD - 1)) != 0) { + xp[ds++] = 0; + } + + if (is_negative) { + *hp = make_neg_bignum_header(sz-1); + } else { + *hp = make_pos_bignum_header(sz-1); + } + return tmp_res; +} + /* ** Estimate the number of decimal digits (include sign) diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index 2afc37004f..256f1c2b45 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -140,6 +140,7 @@ Eterm big_lshift(Eterm, Sint, Eterm*); int big_comp (Wterm, Wterm); int big_ucomp (Eterm, Eterm); int big_to_double(Wterm x, double* resp); +Eterm double_to_big(double, Eterm*); Eterm small_to_big(Sint, Eterm*); Eterm uint_to_big(Uint, Eterm*); Eterm uword_to_big(UWord, Eterm*); diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index e7fd144ec3..ed9139518c 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -55,7 +55,7 @@ heap data on the C stack or if we use the buffers in the scheduler data. */ #define TMP_HEAP_SIZE 128 /* Number of Eterm in the schedulers small heap for transient heap data */ -#define CMP_TMP_HEAP_SIZE 2 /* cmp wants its own tmp-heap... */ +#define CMP_TMP_HEAP_SIZE 16 /* cmp wants its own tmp-heap... */ #define ERL_ARITH_TMP_HEAP_SIZE 4 /* as does erl_arith... */ #define BEAM_EMU_TMP_HEAP_SIZE 2 /* and beam_emu... */ diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 3f6accba2d..e67a793b9a 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -2642,7 +2642,7 @@ tailrecur_ne: FloatDef f1, f2; Eterm big; #if HEAP_ON_C_STACK - Eterm big_buf[2]; /* If HEAP_ON_C_STACK */ + Eterm big_buf[16]; /* If HEAP_ON_C_STACK */ #else Eterm *big_buf = erts_get_scheduler_data()->cmp_tmp_heap; #endif @@ -2661,33 +2661,70 @@ tailrecur_ne: j = big_comp(big, bw); break; case SMALL_FLOAT: - f1.fd = signed_val(a); GET_DOUBLE(bw, f2); - j = float_comp(f1.fd, f2.fd); + if ((f2.fd < 9007199254740990.0 && f2.fd > -9007199254740990.0)) // Float is within the no loss limit + { + f1.fd = signed_val(a); + j = float_comp(f1.fd, f2.fd); + } else if (f2.fd > (double) (MAX_SMALL + 1)) { // Float is a positive bignum, i.e. bigger + j = -1; + } else if (f2.fd < (double) (MIN_SMALL - 1)) { // Float is a negative bignum, i.e. smaller + j = 1; + } else { // Float is a Sint but less precise it + j = signed_val(a) - (Sint) f2.fd; + } break; case BIG_SMALL: big = small_to_big(signed_val(b), big_buf); j = big_comp(aw, big); break; case BIG_FLOAT: - if (big_to_double(aw, &f1.fd) < 0) { + GET_DOUBLE(bw, f2); + if ((f2.fd < 9007199254740990.0 && f2.fd > -9007199254740990.0)) { + if (big_to_double(aw, &f1.fd) < 0) { + j = big_sign(a) ? -1 : 1; + } else { + j = float_comp(f1.fd, f2.fd); + } + } else if ((f2.fd < (double) (MAX_SMALL + 1)) && (f2.fd > (double) (MIN_SMALL - 1))) { // Float is a Sint + j = big_sign(a) ? -1 : 1; + } else if (big_arity(a) > ((1 << 10) / D_EXP)) { // If bignum size is larger than largest/smallest float j = big_sign(a) ? -1 : 1; } else { - GET_DOUBLE(bw, f2); - j = float_comp(f1.fd, f2.fd); + big = double_to_big(f2.fd, big_buf); + j = big_comp(aw, big); } break; case FLOAT_SMALL: GET_DOUBLE(aw, f1); - f2.fd = signed_val(b); - j = float_comp(f1.fd, f2.fd); + if ((f1.fd < 9007199254740990.0 && f1.fd > -9007199254740990.0)) // Float is within the no loss limit + { + f2.fd = signed_val(b); + j = float_comp(f1.fd, f2.fd); + } else if (f1.fd > (double) (MAX_SMALL + 1)) { // Float is a positive bignum, i.e. bigger + j = 1; + } else if (f1.fd < (double) (MIN_SMALL - 1)) { // Float is a negative bignum, i.e. smaller + j = -1; + } else { // Float is a Sint but less precise it + j = (Sint) f1.fd - signed_val(b); + } break; case FLOAT_BIG: - if (big_to_double(bw, &f2.fd) < 0) { + GET_DOUBLE(aw, f1); + if ((f1.fd < 9007199254740990.0 && f1.fd > -9007199254740990.0)) { + if (big_to_double(bw, &f2.fd) < 0) { + j = big_sign(b) ? 1 : -1; + } else { + j = float_comp(f1.fd, f2.fd); + } + } else if ((f1.fd < (double) (MAX_SMALL + 1)) + && (f1.fd > (double) (MIN_SMALL - 1))) { // Float is a Sint + j = big_sign(b) ? 1 : -1; + } else if (big_arity(b) > ((1 << 10) / D_EXP)) { // If bignum size is larger than largest/smallest float j = big_sign(b) ? 1 : -1; } else { - GET_DOUBLE(aw, f1); - j = float_comp(f1.fd, f2.fd); + big = double_to_big(f1.fd, big_buf); + j = big_comp(big, bw); } break; default: -- cgit v1.2.3