shithub: femtolisp

Download patch

ref: 36a209cd5f648d4e7cdac062d8ecb321b561847d
parent: 81641a224004107240a3c7bb4e24b66bf92afb73
author: JeffBezanson <jeff.bezanson@gmail.com>
date: Wed Apr 15 19:54:43 EDT 2009

making = a builtin
fixing = and eqv? to work properly on NaNs
fixing other comparison predicates to be consistent


--- a/femtolisp/compiler.lsp
+++ b/femtolisp/compiler.lsp
@@ -17,7 +17,7 @@
     :cons :list :car :cdr :set-car! :set-cdr!
     :eval :apply
 
-    :+ :- :* :/ :< :compare
+    :+ :- :* :/ := :< :compare
 
     :vector :aref :aset! :for
 
@@ -40,7 +40,8 @@
 	 :set-cdr! 2      :eval     1
 	 :apply    2      :<        2
          :for      3      :compare  2
-         :aref     2      :aset!    3))
+         :aref     2      :aset!    3
+	 :=        2))
 
 (define 1/Instructions (table.invert Instructions))
 
--- a/femtolisp/equal.c
+++ b/femtolisp/equal.c
@@ -34,17 +34,23 @@
 }
 
 // a is a fixnum, b is a cprim
-static value_t compare_num_cprim(value_t a, value_t b, int eq)
+static value_t compare_num_cprim(value_t a, value_t b, int eq, int swap)
 {
     cprim_t *bcp = (cprim_t*)ptr(b);
     numerictype_t bt = cp_numtype(bcp);
     fixnum_t ia = numval(a);
     void *bptr = cp_data(bcp);
-    if (cmp_eq(&ia, T_FIXNUM, bptr, bt))
+    if (cmp_eq(&ia, T_FIXNUM, bptr, bt, 1))
         return fixnum(0);
     if (eq) return fixnum(1);
-    if (cmp_lt(&ia, T_FIXNUM, bptr, bt))
-        return fixnum(-1);
+    if (swap) {
+        if (cmp_lt(bptr, bt, &ia, T_FIXNUM))
+            return fixnum(-1);
+    }
+    else {
+        if (cmp_lt(&ia, T_FIXNUM, bptr, bt))
+            return fixnum(-1);
+    }
     return fixnum(1);
 }
 
@@ -87,7 +93,7 @@
             return (numval(a) < numval(b)) ? fixnum(-1) : fixnum(1);
         }
         if (iscprim(b)) {
-            return compare_num_cprim(a, b, eq);
+            return compare_num_cprim(a, b, eq, 0);
         }
         return fixnum(-1);
     case TAG_SYM:
@@ -104,7 +110,7 @@
             cprim_t *acp=(cprim_t*)ptr(a), *bcp=(cprim_t*)ptr(b);
             numerictype_t at=cp_numtype(acp), bt=cp_numtype(bcp);
             void *aptr=cp_data(acp), *bptr=cp_data(bcp);
-            if (cmp_eq(aptr, at, bptr, bt))
+            if (cmp_eq(aptr, at, bptr, bt, 1))
                 return fixnum(0);
             if (eq) return fixnum(1);
             if (cmp_lt(aptr, at, bptr, bt))
@@ -112,7 +118,7 @@
             return fixnum(1);
         }
         else if (isfixnum(b)) {
-            return fixnum(-numval(compare_num_cprim(b, a, eq)));
+            return compare_num_cprim(b, a, eq, 1);
         }
         break;
     case TAG_CVALUE:
--- a/femtolisp/flisp.c
+++ b/femtolisp/flisp.c
@@ -68,7 +68,7 @@
       "eval", "apply",
 
       // arithmetic
-      "+", "-", "*", "/", "<", "compare",
+      "+", "-", "*", "/", "=", "<", "compare",
 
       // sequences
       "vector", "aref", "aset!", "for",
@@ -649,6 +649,33 @@
     return (isfixnum(v) || iscprim(v));
 }
 
+static int numeric_equals(value_t a, value_t b)
+{
+    value_t tmp;
+    if (isfixnum(b)) {
+        tmp=a; a=b; b=tmp;
+    }
+    void *aptr, *bptr;
+    numerictype_t at, bt;
+    if (!iscprim(b)) type_error("=", "number", b);
+    cprim_t *cp = (cprim_t*)ptr(b);
+    fixnum_t fv;
+    bt = cp_numtype(cp);
+    bptr = cp_data(cp);
+    if (isfixnum(a)) {
+        fv = numval(a);
+        at = T_FIXNUM;
+        aptr = &fv;
+    }
+    else if (iscprim(a)) {
+        cp = (cprim_t*)ptr(a);
+        at = cp_numtype(cp);
+        aptr = cp_data(cp);
+    }
+    else type_error("=", "number", a);
+    return cmp_eq(aptr, at, bptr, bt, 0);
+}
+
 // read -----------------------------------------------------------------------
 
 #include "read.c"
@@ -1289,6 +1316,16 @@
             argcount("compare", nargs, 2);
             v = compare(Stack[SP-2], Stack[SP-1]);
             break;
+        case F_NUMEQ:
+            argcount("=", nargs, 2);
+            v = Stack[SP-2]; e = Stack[SP-1];
+            if (bothfixnums(v, e)) {
+                v = (v == e) ? FL_T : FL_F;
+            }
+            else {
+                v = numeric_equals(v, e) ? FL_T : FL_F;
+            }
+            break;
         case F_LT:
             argcount("<", nargs, 2);
             if (bothfixnums(Stack[SP-2], Stack[SP-1])) {
@@ -1856,6 +1893,17 @@
                 POPN(n);
                 PUSH(v);
             }
+            break;
+        case F_NUMEQ:
+            v = Stack[SP-2]; e = Stack[SP-1];
+            if (bothfixnums(v, e)) {
+                v = (v == e) ? FL_T : FL_F;
+            }
+            else {
+                v = numeric_equals(v, e) ? FL_T : FL_F;
+            }
+            POPN(1);
+            Stack[SP-1] = v;
             break;
         case OP_LT:
             if (bothfixnums(Stack[SP-2], Stack[SP-1])) {
--- a/femtolisp/flisp.h
+++ b/femtolisp/flisp.h
@@ -125,7 +125,7 @@
 
     F_CONS, F_LIST, F_CAR, F_CDR, F_SETCAR, F_SETCDR,
     F_EVAL, F_APPLY,
-    F_ADD, F_SUB, F_MUL, F_DIV, F_LT, F_COMPARE,
+    F_ADD, F_SUB, F_MUL, F_DIV, F_NUMEQ, F_LT, F_COMPARE,
 
     F_VECTOR, F_AREF, F_ASET, F_FOR,
     F_TRUE, F_FALSE, F_NIL,
--- a/femtolisp/opcodes.h
+++ b/femtolisp/opcodes.h
@@ -12,7 +12,7 @@
     OP_CONS, OP_LIST, OP_CAR, OP_CDR, OP_SETCAR, OP_SETCDR,
     OP_EVAL, OP_APPLY,
 
-    OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_LT, OP_COMPARE,
+    OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_NUMEQ, OP_LT, OP_COMPARE,
 
     OP_VECTOR, OP_AREF, OP_ASET, OP_FOR,
 
--- a/femtolisp/system.lsp
+++ b/femtolisp/system.lsp
@@ -86,11 +86,10 @@
 	((eqv?       (caar lst) item) (car lst))
 	(#t          (assv item (cdr lst)))))
 
-(define =   eqv?)
-(define (/= a b) (not (eqv? a b)))
+(define (/= a b) (not (= a b)))
 (define (>  a b) (< b a))
-(define (<= a b) (not (< b a)))
-(define (>= a b) (not (< a b)))
+(define (<= a b) (or (< a b) (= a b)))
+(define (>= a b) (or (< b a) (= a b)))
 (define (negative? x) (< x 0))
 (define (zero? x)     (= x 0))
 (define (positive? x) (> x 0))
--- a/femtolisp/table.c
+++ b/femtolisp/table.c
@@ -171,7 +171,6 @@
     htable_t *h = totable(args[2], "table.foldl");
     size_t i, n = h->size;
     void **table = h->table;
-    value_t c;
     for(i=0; i < n; i+=2) {
         if (table[i+1] != HT_NOTFOUND) {
             args[1] = applyn(3, args[0],
--- a/femtolisp/unittest.lsp
+++ b/femtolisp/unittest.lsp
@@ -69,6 +69,20 @@
 
 (assert (equal (string 'sym #byte(65) #wchar(945) "blah") "symA\u03B1blah"))
 
+; NaNs
+(assert (equal? +nan.0 +nan.0))
+(assert (not (= +nan.0 +nan.0)))
+(assert (not (= +nan.0 -nan.0)))
+(assert (equal? (< +nan.0 3) (> 3 +nan.0)))
+(assert (equal? (< +nan.0 (double 3)) (> (double 3) +nan.0)))
+(assert (equal? (< +nan.0 3) (> (double 3) +nan.0)))
+(assert (equal? (< +nan.0 (double 3)) (> 3 +nan.0)))
+(assert (equal? (< +nan.0 3) (< +nan.0 (double 3))))
+(assert (equal? (> +nan.0 3) (> +nan.0 (double 3))))
+(assert (equal? (< 3 +nan.0) (> +nan.0 (double 3))))
+(assert (equal? (> 3 +nan.0) (> (double 3) +nan.0)))
+(assert (not (>= +nan.0 +nan.0)))
+
 ; this crashed once
 (for 1 10 (lambda (i) 0))
 
--- a/llt/dtypes.h
+++ b/llt/dtypes.h
@@ -116,6 +116,7 @@
 #define LABS(n) (((n)^((n)>>(NBITS-1))) - ((n)>>(NBITS-1)))
 #define NBABS(n,nb) (((n)^((n)>>((nb)-1))) - ((n)>>((nb)-1)))
 #define DFINITE(d) (((*(int64_t*)&(d))&0x7ff0000000000000LL)!=0x7ff0000000000000LL)
+#define DNAN(d) (((*(int64_t*)&(d))&0x7ff8000000000000LL)==0x7ff8000000000000LL)
 
 extern double D_PNAN;
 extern double D_NNAN;
--- a/llt/hashing.c
+++ b/llt/hashing.c
@@ -93,11 +93,11 @@
     ios_init_stdstreams();
 
     D_PNAN = strtod("+NaN",NULL);
-    D_NNAN = strtod("-NaN",NULL);
+    D_NNAN = -strtod("+NaN",NULL);
     D_PINF = strtod("+Inf",NULL);
     D_NINF = strtod("-Inf",NULL);
     F_PNAN = strtof("+NaN",NULL);
-    F_NNAN = strtof("-NaN",NULL);
+    F_NNAN = -strtof("+NaN",NULL);
     F_PINF = strtof("+Inf",NULL);
     F_NINF = strtof("-Inf",NULL);
 }
--- a/llt/operators.c
+++ b/llt/operators.c
@@ -235,16 +235,21 @@
     return 0;
 }
 
-int cmp_eq(void *a, numerictype_t atag, void *b, numerictype_t btag)
+int cmp_eq(void *a, numerictype_t atag, void *b, numerictype_t btag,
+           int equalnans)
 {
-    if (atag==btag)
+    if (atag==btag && !equalnans)
         return cmp_same_eq(a, b, atag);
 
     double da = conv_to_double(a, atag);
     double db = conv_to_double(b, btag);
 
-    if ((int)atag >= T_FLOAT && (int)btag >= T_FLOAT)
+    if ((int)atag >= T_FLOAT && (int)btag >= T_FLOAT) {
+        if (equalnans && DNAN(da)) {
+            return *(uint64_t*)&da == *(uint64_t*)&db;
+        }
         return (da == db);
+    }
 
     if (da != db)
         return 0;
@@ -339,8 +344,8 @@
     assert(cmp_lt(&d, T_DOUBLE, &i64, T_INT64));
     assert(!cmp_lt(&i64, T_INT64, &d, T_DOUBLE));
 
-    assert(!cmp_eq(&d, T_DOUBLE, &i64, T_INT64));
+    assert(!cmp_eq(&d, T_DOUBLE, &i64, T_INT64, 0));
     i64 = DBL_MAXINT;
-    assert(cmp_eq(&d, T_DOUBLE, &i64, T_INT64));
+    assert(cmp_eq(&d, T_DOUBLE, &i64, T_INT64, 0));
 }
 #endif
--- a/llt/utils.h
+++ b/llt/utils.h
@@ -75,7 +75,8 @@
 int cmp_same_lt(void *a, void *b, numerictype_t tag);
 int cmp_same_eq(void *a, void *b, numerictype_t tag);
 int cmp_lt(void *a, numerictype_t atag, void *b, numerictype_t btag);
-int cmp_eq(void *a, numerictype_t atag, void *b, numerictype_t btag);
+int cmp_eq(void *a, numerictype_t atag, void *b, numerictype_t btag,
+           int equalnans);
 
 #ifdef ARCH_X86_64
 #  define LEGACY_REGS "=Q"