aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
authorLukas Larsson <lukas@erlang-solutions.com>2011-07-26 17:11:41 +0200
committerLukas Larsson <lukas@erlang-solutions.com>2011-10-11 17:10:19 +0200
commite04883d448df6e622535020449903e2f2a0f32c9 (patch)
treec7db6e79663481ff9e9f1cb2b79b86cfe61cfec9 /erts/emulator
parent622daf3b96666f8bbabec44a15b26a188a83b95e (diff)
downloadotp-e04883d448df6e622535020449903e2f2a0f32c9.tar.gz
otp-e04883d448df6e622535020449903e2f2a0f32c9.tar.bz2
otp-e04883d448df6e622535020449903e2f2a0f32c9.zip
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.
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/beam/big.c57
-rw-r--r--erts/emulator/beam/big.h1
-rw-r--r--erts/emulator/beam/erl_vm.h2
-rw-r--r--erts/emulator/beam/utils.c59
4 files changed, 107 insertions, 12 deletions
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: