summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2017-12-02 23:19:44 -0600
committerKeith Packard <keithp@keithp.com>2017-12-02 23:19:44 -0600
commit577911241db454bc3129fc47566c6a55752c4182 (patch)
tree32c844fef65d307f472efb9d8a93ea68fa568a68 /src
parentb986a12b478a6d4ff550786d24aa8628dc0abe32 (diff)
altos/lisp: Overflow int computations to float
When an int computation overflows, switch to float. Signed-off-by: Keith Packard <keithp@keithp.com>
Diffstat (limited to 'src')
-rw-r--r--src/lisp/ao_lisp.h2
-rw-r--r--src/lisp/ao_lisp_builtin.c42
2 files changed, 18 insertions, 26 deletions
diff --git a/src/lisp/ao_lisp.h b/src/lisp/ao_lisp.h
index 1f3fb2b4..7cd8b5a5 100644
--- a/src/lisp/ao_lisp.h
+++ b/src/lisp/ao_lisp.h
@@ -208,6 +208,8 @@ ao_lisp_bigint_int(uint32_t bi) {
#define AO_LISP_MIN_INT (-(1 << (15 - AO_LISP_TYPE_SHIFT)))
#define AO_LISP_MAX_INT ((1 << (15 - AO_LISP_TYPE_SHIFT)) - 1)
+#define AO_LISP_MIN_BIGINT (-(1 << 24))
+#define AO_LISP_MAX_BIGINT ((1 << 24) - 1)
#define AO_LISP_NOT_INTEGER 0x7fffffff
diff --git a/src/lisp/ao_lisp_builtin.c b/src/lisp/ao_lisp_builtin.c
index d4751ac2..ad8f4125 100644
--- a/src/lisp/ao_lisp_builtin.c
+++ b/src/lisp/ao_lisp_builtin.c
@@ -321,24 +321,30 @@ ao_lisp_math(struct ao_lisp_cons *orig_cons, enum ao_lisp_builtin_id op)
} else if (ao_lisp_integer_typep(rt) && ao_lisp_integer_typep(ct)) {
int32_t r = ao_lisp_poly_integer(ret);
int32_t c = ao_lisp_poly_integer(car);
+ int64_t t;
switch(op) {
case builtin_plus:
r += c;
+ check_overflow:
+ if (r < AO_LISP_MIN_BIGINT || AO_LISP_MAX_BIGINT < r)
+ goto inexact;
break;
case builtin_minus:
r -= c;
+ goto check_overflow;
break;
case builtin_times:
- r *= c;
+ t = (int64_t) r * (int64_t) c;
+ if (t < AO_LISP_MIN_BIGINT || AO_LISP_MAX_BIGINT < t)
+ goto inexact;
+ r = (int32_t) t;
break;
case builtin_divide:
if (c != 0 && (r % c) == 0)
r /= c;
- else {
- ret = ao_lisp_float_get((float) r / (float) c);
- continue;
- }
+ else
+ goto inexact;
break;
case builtin_quotient:
if (c == 0)
@@ -365,8 +371,10 @@ ao_lisp_math(struct ao_lisp_cons *orig_cons, enum ao_lisp_builtin_id op)
}
ret = ao_lisp_integer_poly(r);
} else if (ao_lisp_number_typep(rt) && ao_lisp_number_typep(ct)) {
- float r = ao_lisp_poly_number(ret);
- float c = ao_lisp_poly_number(car);
+ float r, c;
+ inexact:
+ r = ao_lisp_poly_number(ret);
+ c = ao_lisp_poly_number(car);
switch(op) {
case builtin_plus:
r += c;
@@ -380,28 +388,10 @@ ao_lisp_math(struct ao_lisp_cons *orig_cons, enum ao_lisp_builtin_id op)
case builtin_divide:
r /= c;
break;
-#if 0
case builtin_quotient:
- if (c == 0)
- return ao_lisp_error(AO_LISP_DIVIDE_BY_ZERO, "quotient by zero");
- if (r % c != 0 && (c < 0) != (r < 0))
- r = r / c - 1;
- else
- r = r / c;
- break;
case builtin_remainder:
- if (c == 0)
- return ao_lisp_error(AO_LISP_DIVIDE_BY_ZERO, "remainder by zero");
- r %= c;
- break;
case builtin_modulo:
- if (c == 0)
- return ao_lisp_error(AO_LISP_DIVIDE_BY_ZERO, "modulo by zero");
- r %= c;
- if ((r < 0) != (c < 0))
- r += c;
- break;
-#endif
+ return ao_lisp_error(AO_LISP_INVALID, "non-integer value in integer divide");
default:
break;
}