commit bd16dd46d4df3c1cbd6fb73b38bad788c422bd43
parent adeac8c2645a06ece901d69c406de6a88dcf9a26
Author: Ed van Bruggen <edvb@uw.edu>
Date: Thu, 24 Jan 2019 23:54:16 -0800
Internally store integers and decimals as a ratio
Makes arithmetic with different types easier and more general.
Diffstat:
tib/math.c | | | 164 | +++++++++++++++++++++++++++++-------------------------------------------------- |
tisp.c | | | 21 | ++++++++++----------- |
tisp.h | | | 13 | ++++++------- |
3 files changed, 75 insertions(+), 123 deletions(-)
diff --git a/tib/math.c b/tib/math.c
@@ -29,14 +29,38 @@
tsp_arg_type(A, NAME, TYPE); \
} while(0)
-#define RATIO_DUB(A, OP, B) return mk_dub(((double)A->v.r.num/A->v.r.den) OP B->v.n)
-#define RATIO_INT(A, OP, B) return mk_rat(A->v.r.num OP (B->v.n * A->v.r.den), A->v.r.den)
-#define RATIO_INT2(A, OP, B) return mk_rat(A->v.r.num OP B->v.n, A->v.r.den)
-#define COMBINE_FIN(A, OP, B) do { \
- if (A->t & DOUBLE || B->t & DOUBLE) \
- return mk_dub(A->v.n OP B->v.n); \
- return mk_int(A->v.n OP B->v.n); \
-} while(0)
+/* Wrapper functions to be returned by mk_num, all need same arguments */
+static Val
+create_int(double num, double den)
+{
+ assert(den == 1);
+ return mk_int(num);
+}
+
+static Val
+create_dub(double num, double den)
+{
+ assert(den == 1);
+ return mk_dub(num);
+}
+
+static Val
+create_rat(double num, double den)
+{
+ return mk_rat(num, den);
+}
+
+/* Return pointer to one of the preceding functions depending on what sort
+ * number should be created by the following arithmetic functions */
+static Val
+(*mk_num(Type a, Type b, int isfrac))(double, double)
+{
+ if (a & DOUBLE || b & DOUBLE)
+ return &create_dub;
+ if (isfrac || a & RATIO || b & RATIO)
+ return &create_rat;
+ return &create_int;
+}
static Val
prim_add(Env env, Val args)
@@ -45,27 +69,10 @@ prim_add(Env env, Val args)
tsp_arg_num(args, "+", 2);
EVAL_CHECK(a, car(args), "+", NUMBER);
EVAL_CHECK(b, car(cdr(args)), "+", NUMBER);
- switch (a->t) {
- case RATIO:
- switch (b->t) {
- case RATIO:
- return mk_rat(a->v.r.num * b->v.r.den + a->v.r.den * b->v.r.num,
- a->v.r.den * b->v.r.den);
- case INTEGER:
- RATIO_INT(a, +, b);
- case DOUBLE:
- RATIO_DUB(a, +, b);
- default: break;
- }
- case INTEGER:
- if (b->t & RATIO)
- RATIO_INT(b, +, a);
- case DOUBLE:
- if (b->t & RATIO)
- RATIO_DUB(b, +, a);
- default: break;
- }
- COMBINE_FIN(a, +, b);
+ if (a->t & DOUBLE || b->t & DOUBLE)
+ return mk_dub((a->v.n.num/a->v.n.den) + (b->v.n.num/b->v.n.den));
+ return (mk_num(a->t, b->t, 0))
+ (a->v.n.num * b->v.n.den + a->v.n.den * b->v.n.num, a->v.n.den * b->v.n.den);
}
static Val
@@ -82,27 +89,9 @@ prim_sub(Env env, Val args)
} else {
EVAL_CHECK(b, car(cdr(args)), "-", NUMBER);
}
- switch (a->t) {
- case RATIO:
- switch (b->t) {
- case RATIO:
- return mk_rat(a->v.r.num * b->v.r.den - a->v.r.den * b->v.r.num,
- a->v.r.den * b->v.r.den);
- case INTEGER:
- RATIO_INT(a, -, b);
- case DOUBLE:
- RATIO_DUB(a, -, b);
- default: break;
- }
- case INTEGER:
- if (b->t & RATIO)
- return mk_rat((a->v.n * b->v.r.den) - b->v.r.num, b->v.r.den);
- case DOUBLE:
- if (b->t & RATIO)
- return mk_dub(a->v.n - ((double)b->v.r.num/b->v.r.den));
- default: break;
- }
- COMBINE_FIN(a, -, b);
+ if (a->t & DOUBLE || b->t & DOUBLE)
+ return mk_dub((a->v.n.num/a->v.n.den) - (b->v.n.num/b->v.n.den));
+ return (mk_num(a->t, b->t, 0))(a->v.n.num * b->v.n.den - a->v.n.den * b->v.n.num, a->v.n.den * b->v.n.den);
}
static Val
@@ -112,26 +101,10 @@ prim_mul(Env env, Val args)
tsp_arg_num(args, "*", 2);
EVAL_CHECK(a, car(args), "*", NUMBER);
EVAL_CHECK(b, car(cdr(args)), "*", NUMBER);
- switch (a->t) {
- case RATIO:
- switch (b->t) {
- case RATIO:
- return mk_rat(a->v.r.num * b->v.r.num, a->v.r.den * b->v.r.den);
- case INTEGER:
- RATIO_INT2(a, *, b);
- case DOUBLE:
- RATIO_DUB(a, *, b);
- default: break;
- }
- case INTEGER:
- if (b->t & RATIO)
- RATIO_INT2(b, *, a);
- case DOUBLE:
- if (b->t & RATIO)
- RATIO_DUB(b, *, a);
- default: break;
- }
- COMBINE_FIN(a, *, b);
+ if (a->t & DOUBLE || b->t & DOUBLE)
+ return mk_dub((a->v.n.num/a->v.n.den) * (b->v.n.num/b->v.n.den));
+ return (mk_num(a->t, b->t, 0))(a->v.n.num * b->v.n.num, a->v.n.den * b->v.n.den);
+
}
static Val
@@ -141,28 +114,9 @@ prim_div(Env env, Val args)
tsp_arg_num(args, "/", 2);
EVAL_CHECK(a, car(args), "/", NUMBER);
EVAL_CHECK(b, car(cdr(args)), "/", NUMBER);
- switch (a->t) {
- case RATIO:
- switch (b->t) {
- case RATIO:
- return mk_rat(a->v.r.num * b->v.r.den, a->v.r.den * b->v.r.num);
- case INTEGER:
- return mk_rat(a->v.r.num, a->v.r.den * b->v.n);
- case DOUBLE:
- RATIO_DUB(a, /, b);
- default: break;
- }
- case INTEGER:
- if (b->t & RATIO)
- return mk_rat(b->v.r.den * a->v.n, b->v.r.num);
- case DOUBLE:
- if (b->t & RATIO)
- return mk_dub(a->v.n / ((double)b->v.r.num/b->v.r.den));
- default: break;
- }
if (a->t & DOUBLE || b->t & DOUBLE)
- return mk_dub(a->v.n / b->v.n);
- return mk_rat(a->v.n, b->v.n);
+ return mk_dub((a->v.n.num/a->v.n.den) / (b->v.n.num/b->v.n.den));
+ return (mk_num(a->t, b->t, 1))(a->v.n.num * b->v.n.den, a->v.n.den * b->v.n.num);
}
static Val
@@ -172,23 +126,23 @@ prim_mod(Env env, Val args)
tsp_arg_num(args, "mod", 2);
EVAL_CHECK(a, car(args), "mod", INTEGER);
EVAL_CHECK(b, car(cdr(args)), "mod", INTEGER);
- if (b->v.n == 0)
+ if (b->v.n.num == 0)
tsp_warn("division by zero");
- return mk_int((int)a->v.n % abs((int)b->v.n));
+ return mk_int((int)a->v.n.num % abs((int)b->v.n.num));
}
-#define PRIM_COMPARE(NAME, OP, FUNC) \
-static Val \
-prim_##NAME(Env env, Val args) \
-{ \
- Val v; \
- if (!(v = tisp_eval_list(env, args))) \
- return NULL; \
- if (list_len(v) != 2) \
- return env->t; \
- tsp_arg_type(car(v), FUNC, INTEGER); \
- tsp_arg_type(car(cdr(v)), FUNC, INTEGER); \
- return (car(v)->v.n OP car(cdr(v))->v.n) ? env->t : env->nil; \
+#define PRIM_COMPARE(NAME, OP, FUNC) \
+static Val \
+prim_##NAME(Env env, Val args) \
+{ \
+ Val v; \
+ if (!(v = tisp_eval_list(env, args))) \
+ return NULL; \
+ if (list_len(v) != 2) \
+ return env->t; \
+ tsp_arg_type(car(v), FUNC, INTEGER); \
+ tsp_arg_type(car(cdr(v)), FUNC, INTEGER); \
+ return (car(v)->v.n.num OP car(cdr(v))->v.n.num) ? env->t : env->nil; \
}
PRIM_COMPARE(lt, <, "<")
diff --git a/tisp.c b/tisp.c
@@ -150,11 +150,8 @@ vals_eq(Val a, Val b)
switch (a->t) {
case INTEGER:
case DOUBLE:
- if (a->v.n != b->v.n)
- return 0;
- break;
case RATIO:
- if (a->v.r.num != b->v.r.num || a->v.r.den != b->v.r.den)
+ if (a->v.n.num != b->v.n.num || a->v.n.den != b->v.n.den)
return 0;
break;
default: /* PRIMITIVE, STRING, SYMBOL */
@@ -305,7 +302,8 @@ mk_int(int i)
{
Val ret = emalloc(sizeof(struct Val));
ret->t = INTEGER;
- ret->v.n = i;
+ ret->v.n.num = i;
+ ret->v.n.den = 1;
return ret;
}
@@ -314,7 +312,8 @@ mk_dub(double d)
{
Val ret = emalloc(sizeof(struct Val));
ret->t = DOUBLE;
- ret->v.n = d;
+ ret->v.n.num = d;
+ ret->v.n.den = 1;
return ret;
}
@@ -332,7 +331,7 @@ mk_rat(int num, int den)
return mk_int(num);
Val ret = emalloc(sizeof(struct Val));
ret->t = RATIO;
- ret->v.r = (Ratio){ num, den };
+ ret->v.n = (Ratio){ num, den };
return ret;
}
@@ -616,15 +615,15 @@ tisp_print(FILE *f, Val v)
fprintf(f, "()");
break;
case INTEGER:
- fprintf(f, "%d", (int)v->v.n);
+ fprintf(f, "%d", (int)v->v.n.num);
break;
case DOUBLE:
- fprintf(f, "%.15g", v->v.n);
- if (v->v.n == (int)v->v.n)
+ fprintf(f, "%.15g", v->v.n.num);
+ if (v->v.n.num == (int)v->v.n.num)
fprintf(f, ".0");
break;
case RATIO:
- fprintf(f, "%d/%d", v->v.r.num, v->v.r.den);
+ fprintf(f, "%d/%d", (int)v->v.n.num, (int)v->v.n.den);
break;
case STRING:
fprintf(f, "\"%s\"", v->v.s);
diff --git a/tisp.h b/tisp.h
@@ -59,7 +59,7 @@ typedef enum {
/* fraction */
typedef struct {
- int num, den;
+ double num, den;
} Ratio;
typedef struct Entry *Entry;
@@ -104,12 +104,11 @@ static Type const NUMBER = INTEGER | DOUBLE | RATIO;
struct Val {
Type t; /* NONE, NIL */
union {
- double n; /* INTEGER, DOUBLE */
- Ratio r; /* RATIO */
- char *s; /* STRING, SYMBOL */
- Prim pr; /* PRIMITIVE */
- Func f; /* FUNCTION */
- Pair p; /* PAIR */
+ Ratio n; /* INTEGER, DOUBLE, RATIO */
+ char *s; /* STRING, SYMBOL */
+ Prim pr; /* PRIMITIVE */
+ Func f; /* FUNCTION */
+ Pair p; /* PAIR */
} v;
};