shithub: femtolisp

Download patch

ref: 6ed61e66ac92b07c5424b0364d1c7589e1ddd437
parent: 689ec946d8650d617f72359e2c997ce2dfa61d3f
author: Jeff Bezanson <bezanson@post.harvard.edu>
date: Fri Feb 17 18:59:45 EST 2012

moving some flisp-specific code out of library

--- a/cvalues.c
+++ b/cvalues.c
@@ -1,3 +1,5 @@
+#include "operators.c"
+
 #ifdef BITS64
 #define NWORDS(sz) (((sz)+7)>>3)
 #else
--- a/flisp.h
+++ b/flisp.h
@@ -208,6 +208,19 @@
 } cvtable_t;
 
 /* functions needed to implement the value interface (cvtable_t) */
+typedef enum { T_INT8, T_UINT8, T_INT16, T_UINT16, T_INT32, T_UINT32,
+               T_INT64, T_UINT64, T_FLOAT, T_DOUBLE } numerictype_t;
+
+#define N_NUMTYPES ((int)T_DOUBLE+1)
+
+#ifdef BITS64
+# define T_LONG T_INT64
+# define T_ULONG T_UINT64
+#else
+# define T_LONG T_INT32
+# define T_ULONG T_UINT32
+#endif
+
 value_t relocate_lispvalue(value_t v);
 void print_traverse(value_t v);
 void fl_print_chr(char c, ios_t *f);
@@ -350,6 +363,21 @@
 value_t mk_wchar(int32_t n);
 value_t return_from_uint64(uint64_t Uaccum);
 value_t return_from_int64(int64_t Saccum);
+
+numerictype_t effective_numerictype(double r);
+double conv_to_double(void *data, numerictype_t tag);
+void conv_from_double(void *data, double d, numerictype_t tag);
+int64_t conv_to_int64(void *data, numerictype_t tag);
+uint64_t conv_to_uint64(void *data, numerictype_t tag);
+int32_t conv_to_int32(void *data, numerictype_t tag);
+uint32_t conv_to_uint32(void *data, numerictype_t tag);
+#ifdef BITS64
+#define conv_to_long conv_to_int64
+#define conv_to_ulong conv_to_uint64
+#else
+#define conv_to_long conv_to_int32
+#define conv_to_ulong conv_to_uint32
+#endif
 
 typedef struct {
     char *name;
--- a/llt/Makefile
+++ b/llt/Makefile
@@ -1,14 +1,11 @@
 CC = gcc
 
-SRCS = bitvector.c hashing.c socket.c timefuncs.c dblprint.c ptrhash.c \
-	utf8.c ios.c operators.c cplxprint.c dirpath.c htable.c \
-	bitvector-ops.c int2str.c dump.c random.c bswap.c memalign.c \
-	swapreverse.c lltinit.c arraylist.c
+SRCS = bitvector.c hashing.c socket.c timefuncs.c ptrhash.c utf8.c ios.c \
+	dirpath.c htable.c bitvector-ops.c int2str.c dump.c random.c \
+	lltinit.c arraylist.c
 OBJS = $(SRCS:%.c=%.o)
 DOBJS = $(SRCS:%.c=%.do)
 TARGET = libllt.a
-TESTSRC = unittest.c
-TESTER = llttest
 
 # OS flags: LINUX, WIN32, MACOSX
 # architecture flags: __CPU__=xxx, BITS64, ARCH_X86, ARCH_X86_64
@@ -34,12 +31,6 @@
 	rm -rf $(TARGET)
 	ar rs $(TARGET) $(OBJS)
 
-test:
-	make clean
-	make debug CFLAGS=-DENABLE_LLT_TEST
-	gcc $(TESTSRC) $(TARGET) -o $(TESTER) -lm
-	./$(TESTER)
-
 clean:
 	rm -f *.o
 	rm -f *.do
@@ -46,4 +37,3 @@
 	rm -f *~
 	rm -f core*
 	rm -f $(TARGET)
-	rm -f $(TESTER)
--- a/llt/bswap.c
+++ /dev/null
@@ -1,87 +1,0 @@
-#include <stdlib.h>
-#include <string.h>
-#include <stddef.h>
-#include <alloca.h>
-#include "dtypes.h"
-#include "utils.h"
-
-void bswap_buffer(byte_t *data, size_t sz, size_t npts)
-{
-    size_t i, b;
-    byte_t *el;
-    byte_t temp;
-
-    if (sz <= 1)
-        return;
-
-    switch (sz) {
-    case 8:
-        for(i=0; i < npts; i++) {
-            ((u_int64_t*)data)[i] = bswap_64(((u_int64_t*)data)[i]);
-        }
-        break;
-    case 4:
-        for(i=0; i < npts; i++) {
-            ((u_int32_t*)data)[i] = bswap_32(((u_int32_t*)data)[i]);
-        }
-        break;
-    case 2:
-        for(i=0; i < npts; i++) {
-            ((u_int16_t*)data)[i] = bswap_16(((u_int16_t*)data)[i]);
-        }
-        break;
-    default:
-        for(i=0; i < sz * npts; i += sz) {
-            el = data + i;
-            for(b=0; b < sz/2; b++) {
-                temp = el[b];
-                el[b] = el[sz-b-1];
-                el[sz-b-1] = temp;
-            }
-        }
-    }
-}
-
-void bswap(byte_t *s, size_t n)
-{
-    unsigned int i;
-    char temp;
-
-    switch (n) {
-    case 8:
-        *(u_int64_t*)s = bswap_64(*(u_int64_t*)s); break;
-    case 4:
-        *(u_int32_t*)s = bswap_32(*(u_int32_t*)s); break;
-    case 2:
-        *(u_int16_t*)s = bswap_16(*(u_int16_t*)s); break;
-    case 1:
-        break;
-    default:
-        for(i=0; i < n/2; i++) {
-            temp = s[i];
-            s[i] = s[n-i-1];
-            s[n-i-1] = temp;
-        }
-    }
-}
-
-void bswap_to(byte_t *dest, byte_t *src, size_t n)
-{
-    unsigned int i;
-
-    switch (n) {
-    case 8:
-        *(u_int64_t*)dest = bswap_64(*(u_int64_t*)src); break;
-    case 4:
-        *(u_int32_t*)dest = bswap_32(*(u_int32_t*)src); break;
-    case 2:
-        *(u_int16_t*)dest = bswap_16(*(u_int16_t*)src); break;
-    case 1:
-        break;
-    default:
-        for(i=0; i < n; i++) {
-            dest[i] = src[n-i-1];
-        }
-    }
-}
-
--- a/llt/cplxprint.c
+++ /dev/null
@@ -1,66 +1,0 @@
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-#include "dtypes.h"
-#include "utils.h"
-
-void snprint_cplx(char *s, size_t cnt, double re, double im,
-                  // args to pass on to snprint_real
-                  int width, int dec,
-                  int max_digs_rt, int max_digs_lf,
-                  // print spaces around sign in a+bi
-                  int spflag)
-{
-    int fzr = (re==0) || rel_zero(re,im);
-    int fzi = (im==0) || rel_zero(im,re);
-    size_t len, sl;
-    size_t space = cnt;
-
-    s[0] = '\0';
-    if (isnan(im) && fzr) {
-        if (space < 2) return;
-        snprint_real(s, space-2, im, width, dec, max_digs_rt, max_digs_lf);
-        strcat(s, "i");
-        return;
-    }
-    if (!fzr || (fzr && fzi)) {
-        if (space < 4) return;
-        snprint_real(s, space-4, re, width, dec, max_digs_rt, max_digs_lf);
-        if ((im >= 0 || (isnan(im)&&!sign_bit(im))) && !fzi) {
-            if (spflag) {
-                strcat(s, " + ");
-            }
-            else {
-                strcat(s, "+");
-            }
-        }
-        else if (!fzi) {
-            im = -im;
-            if (spflag)
-                strcat(s, " - ");
-            else
-                strcat(s, "-");
-        }
-    }
-    if (!fzi) {
-        len = sl = strlen(s);
-        if (im == -1) {
-            while ((long)(len-sl) < (long)(width-2) && len < (space-3))
-                s[len++] = ' ';
-            s[len] =   '-';
-            s[len+1] = 'i';
-            s[len+2] = '\0';
-        }
-        else if (im == 1) {
-            while ((long)(len-sl) < (long)(width-1) && len < (space-2))
-                s[len++] = ' ';
-            s[len] =   'i';
-            s[len+1] = '\0';
-        }
-        else {
-            snprint_real(s+len, space-len-2, im, width, dec,
-                         max_digs_rt, max_digs_lf);
-            strcat(s, "i");
-        }
-    }
-}
--- a/llt/dblprint.c
+++ /dev/null
@@ -1,122 +1,0 @@
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include "dtypes.h"
-#include "ieee754.h"
-#include "utils.h"
-
-int double_exponent(double d)
-{
-    union ieee754_double dl;
-
-    dl.d = d;
-    return dl.ieee.exponent - IEEE754_DOUBLE_BIAS;
-}
-
-double double_mantissa(double d)
-{
-    union ieee754_double dl;
-
-    dl.d = d;
-    dl.ieee.exponent = IEEE754_DOUBLE_BIAS;
-    dl.ieee.negative = 0;
-    return dl.d;
-}
-
-int float_exponent(float f)
-{
-    union ieee754_float fl;
-
-    fl.f = f;
-    return fl.ieee.exponent - IEEE754_FLOAT_BIAS;
-}
-
-float float_mantissa(float f)
-{
-    union ieee754_float fl;
-
-    fl.f = f;
-    fl.ieee.exponent = IEEE754_FLOAT_BIAS;
-    fl.ieee.negative = 0;
-    return fl.f;
-}
-
-void snprint_real(char *s, size_t cnt, double r,
-                  int width,    // printf field width, or 0
-                  int dec,      // # decimal digits desired, recommend 16
-                  // # of zeros in .00...0x before using scientific notation
-                  // recommend 3-4 or so
-                  int max_digs_rt,
-                  // # of digits left of decimal before scientific notation
-                  // recommend 10
-                  int max_digs_lf)
-{
-    int mag;
-    double fpart, temp;
-    char format[8];
-    char num_format[3];
-    int sz, keepz=0;
-
-    s[0] = '\0';
-    if (width == -1) {
-        width = 0;
-        keepz=1;
-    }
-    if (isnan(r)) {
-        if (sign_bit(r))
-            strncpy(s, "-nan", cnt);
-        else
-            strncpy(s, "nan", cnt);
-        return;
-    }
-    if (r == 0) {
-        strncpy(s, "0", cnt);
-        return;
-    }
-
-    num_format[0] = 'l';
-    num_format[2] = '\0';
-
-    mag = double_exponent(r);
-
-    mag = (int)(((double)mag)/LOG2_10 + 0.5);
-    if (r == 0)
-        mag = 0;
-    if ((mag > max_digs_lf-1) || (mag < -max_digs_rt)) {
-        num_format[1] = 'e';
-        temp = r/pow(10, mag);      /* see if number will have a decimal */
-        fpart = temp - floor(temp); /* when written in scientific notation */
-    }
-    else {
-        num_format[1] = 'f';
-        fpart = r - floor(r);
-    }
-    if (fpart == 0)
-        dec = 0;
-    if (width == 0) {
-        snprintf(format, 8, "%%.%d%s", dec, num_format);
-    }
-    else {
-        snprintf(format, 8, "%%%d.%d%s", width, dec, num_format);
-    }
-    sz = snprintf(s, cnt, format, r);
-    /* trim trailing zeros from fractions. not when using scientific
-       notation, since we might have e.g. 1.2000e+100. also not when we
-       need a specific output width */
-    if (width == 0 && !keepz) {
-        if (sz > 2 && fpart && num_format[1]!='e') {
-            while (s[sz-1] == '0') {
-                s[sz-1]='\0';
-                sz--;
-            }
-            // don't need trailing .
-            if (s[sz-1] == '.') {
-                s[sz-1] = '\0';
-                sz--;
-            }
-        }
-    }
-    // TODO. currently 1.1e20 prints as 1.1000000000000000e+20; be able to
-    // get rid of all those zeros.
-}
--- a/llt/dtypes.h
+++ b/llt/dtypes.h
@@ -178,17 +178,4 @@
 extern float  F_PINF;
 extern float  F_NINF;
 
-typedef enum { T_INT8, T_UINT8, T_INT16, T_UINT16, T_INT32, T_UINT32,
-               T_INT64, T_UINT64, T_FLOAT, T_DOUBLE } numerictype_t;
-
-#define N_NUMTYPES ((int)T_DOUBLE+1)
-
-#ifdef BITS64
-# define T_LONG T_INT64
-# define T_ULONG T_UINT64
-#else
-# define T_LONG T_INT32
-# define T_ULONG T_UINT32
-#endif
-
 #endif
--- a/llt/memalign.c
+++ /dev/null
@@ -1,50 +1,0 @@
-#include <stdlib.h>
-#include <string.h>
-#include <stddef.h>
-#include <alloca.h>
-#include "dtypes.h"
-#include "utils.h"
-
-#define ALIGNED_TO_ACTUAL(p) (((char*)p) - ((long*)p)[-1])
-
-static void *aligned_ptr(char *ptr, size_t align_size)
-{
-    char *ptr2, *aligned_ptr;
-
-    ptr2 = ptr + sizeof(long);
-    aligned_ptr = (char*)LLT_ALIGN(((uptrint_t)ptr2), align_size);
-
-    ((long*)aligned_ptr)[-1] = (long)(aligned_ptr - ptr);
-
-    return aligned_ptr;
-}
-
-/* align_size has to be a power of two */
-void *malloc_aligned(size_t size, size_t align_size)
-{
-    char *ptr;
-
-    ptr = (char*)LLT_ALLOC(size + align_size-1 + sizeof(long));
-    if (ptr == NULL)
-        return NULL;
-
-    return aligned_ptr(ptr, align_size);
-}
-
-void free_aligned(void *ptr)
-{
-    LLT_FREE(ALIGNED_TO_ACTUAL(ptr));
-}
-
-void *realloc_aligned(void *ptr, size_t size, size_t align_size)
-{
-    char *pnew;
-
-    if (ptr != NULL)
-        ptr = ALIGNED_TO_ACTUAL(ptr);
-    pnew = LLT_REALLOC(ptr, size + align_size-1 + sizeof(long));
-    if (pnew == NULL)
-        return NULL;
-
-    return aligned_ptr(pnew, align_size);
-}
--- a/llt/operators.c
+++ /dev/null
@@ -1,355 +1,0 @@
-#include <limits.h>
-#include <assert.h>
-#include "dtypes.h"
-#include "utils.h"
-#include "ieee754.h"
-
-// given a number, determine an appropriate type for storing it
-#if 0
-numerictype_t effective_numerictype(double r)
-{
-    double fp;
-
-    fp = fpart(r);
-    if (fp != 0 || r > U64_MAX || r < S64_MIN) {
-        if (r > FLT_MAX || r < -FLT_MAX || (fabs(r) < FLT_MIN)) {
-            return T_DOUBLE;
-        }
-        else {
-            return T_FLOAT;
-        }
-    }
-    else if (r >= SCHAR_MIN && r <= SCHAR_MAX) {
-        return T_INT8;
-    }
-    else if (r >= SHRT_MIN && r <= SHRT_MAX) {
-        return T_INT16;
-    }
-    else if (r >= INT_MIN && r <= INT_MAX) {
-        return T_INT32;
-    }
-    else if (r <= S64_MAX) {
-        return T_INT64;
-    }
-    return T_UINT64;
-}
-#else
-// simpler version implementing a smaller preferred type repertoire
-numerictype_t effective_numerictype(double r)
-{
-    double fp;
-
-    fp = fpart(r);
-    if (fp != 0 || r > U64_MAX || r < S64_MIN) {
-        return T_DOUBLE;
-    }
-    else if (r >= INT_MIN && r <= INT_MAX) {
-        return T_INT32;
-    }
-    else if (r <= S64_MAX) {
-        return T_INT64;
-    }
-    return T_UINT64;
-}
-#endif
-
-double conv_to_double(void *data, numerictype_t tag)
-{
-    double d=0;
-    switch (tag) {
-    case T_INT8:   d = (double)*(int8_t*)data; break;
-    case T_UINT8:  d = (double)*(uint8_t*)data; break;
-    case T_INT16:  d = (double)*(int16_t*)data; break;
-    case T_UINT16: d = (double)*(uint16_t*)data; break;
-    case T_INT32:  d = (double)*(int32_t*)data; break;
-    case T_UINT32: d = (double)*(uint32_t*)data; break;
-    case T_INT64:
-        d = (double)*(int64_t*)data;
-        if (d > 0 && *(int64_t*)data < 0)  // can happen!
-            d = -d;
-        break;
-    case T_UINT64: d = (double)*(uint64_t*)data; break;
-    case T_FLOAT:  d = (double)*(float*)data; break;
-    case T_DOUBLE: return *(double*)data;
-    }
-    return d;
-}
-
-void conv_from_double(void *dest, double d, numerictype_t tag)
-{
-    switch (tag) {
-    case T_INT8:   *(int8_t*)dest = d; break;
-    case T_UINT8:  *(uint8_t*)dest = d; break;
-    case T_INT16:  *(int16_t*)dest = d; break;
-    case T_UINT16: *(uint16_t*)dest = d; break;
-    case T_INT32:  *(int32_t*)dest = d; break;
-    case T_UINT32: *(uint32_t*)dest = d; break;
-    case T_INT64:
-        *(int64_t*)dest = d;
-        if (d > 0 && *(int64_t*)dest < 0)  // 0x8000000000000000 is a bitch
-            *(int64_t*)dest = S64_MAX;
-        break;
-    case T_UINT64: *(uint64_t*)dest = (int64_t)d; break;
-    case T_FLOAT:  *(float*)dest = d; break;
-    case T_DOUBLE: *(double*)dest = d; break;
-    }
-}
-
-#define CONV_TO_INTTYPE(type)                               \
-type##_t conv_to_##type(void *data, numerictype_t tag)      \
-{                                                           \
-    type##_t i=0;                                           \
-    switch (tag) {                                          \
-    case T_INT8:   i = (type##_t)*(int8_t*)data; break;     \
-    case T_UINT8:  i = (type##_t)*(uint8_t*)data; break;    \
-    case T_INT16:  i = (type##_t)*(int16_t*)data; break;    \
-    case T_UINT16: i = (type##_t)*(uint16_t*)data; break;   \
-    case T_INT32:  i = (type##_t)*(int32_t*)data; break;    \
-    case T_UINT32: i = (type##_t)*(uint32_t*)data; break;   \
-    case T_INT64:  i = (type##_t)*(int64_t*)data; break;    \
-    case T_UINT64: i = (type##_t)*(uint64_t*)data; break;   \
-    case T_FLOAT:  i = (type##_t)*(float*)data; break;      \
-    case T_DOUBLE: i = (type##_t)*(double*)data; break;     \
-    }                                                       \
-    return i;                                               \
-}
-
-CONV_TO_INTTYPE(int64)
-CONV_TO_INTTYPE(int32)
-CONV_TO_INTTYPE(uint32)
-
-// this is needed to work around a possible compiler bug
-// casting negative floats and doubles to uint64. you need
-// to cast to int64 first.
-uint64_t conv_to_uint64(void *data, numerictype_t tag)
-{
-    uint64_t i=0;
-    switch (tag) {
-    case T_INT8:   i = (uint64_t)*(int8_t*)data; break;
-    case T_UINT8:  i = (uint64_t)*(uint8_t*)data; break;
-    case T_INT16:  i = (uint64_t)*(int16_t*)data; break;
-    case T_UINT16: i = (uint64_t)*(uint16_t*)data; break;
-    case T_INT32:  i = (uint64_t)*(int32_t*)data; break;
-    case T_UINT32: i = (uint64_t)*(uint32_t*)data; break;
-    case T_INT64:  i = (uint64_t)*(int64_t*)data; break;
-    case T_UINT64: i = (uint64_t)*(uint64_t*)data; break;
-    case T_FLOAT:
-        if (*(float*)data >= 0)
-            i = (uint64_t)*(float*)data;
-        else
-            i = (uint64_t)(int64_t)*(float*)data;
-        break;
-    case T_DOUBLE:
-        if (*(double*)data >= 0)
-            i = (uint64_t)*(double*)data;
-        else
-            i = (uint64_t)(int64_t)*(double*)data;
-        break;
-    }
-    return i;
-}
-
-int cmp_same_lt(void *a, void *b, numerictype_t tag)
-{
-    switch (tag) {
-    case T_INT8:   return *(int8_t*)a < *(int8_t*)b;
-    case T_UINT8:  return *(uint8_t*)a < *(uint8_t*)b;
-    case T_INT16:  return *(int16_t*)a < *(int16_t*)b;
-    case T_UINT16: return *(uint16_t*)a < *(uint16_t*)b;
-    case T_INT32:  return *(int32_t*)a < *(int32_t*)b;
-    case T_UINT32: return *(uint32_t*)a < *(uint32_t*)b;
-    case T_INT64:  return *(int64_t*)a < *(int64_t*)b;
-    case T_UINT64: return *(uint64_t*)a < *(uint64_t*)b;
-    case T_FLOAT:  return *(float*)a < *(float*)b;
-    case T_DOUBLE: return *(double*)a < *(double*)b;
-    }
-    return 0;
-}
-
-int cmp_same_eq(void *a, void *b, numerictype_t tag)
-{
-    switch (tag) {
-    case T_INT8:   return *(int8_t*)a == *(int8_t*)b;
-    case T_UINT8:  return *(uint8_t*)a == *(uint8_t*)b;
-    case T_INT16:  return *(int16_t*)a == *(int16_t*)b;
-    case T_UINT16: return *(uint16_t*)a == *(uint16_t*)b;
-    case T_INT32:  return *(int32_t*)a == *(int32_t*)b;
-    case T_UINT32: return *(uint32_t*)a == *(uint32_t*)b;
-    case T_INT64:  return *(int64_t*)a == *(int64_t*)b;
-    case T_UINT64: return *(uint64_t*)a == *(uint64_t*)b;
-    case T_FLOAT:  return *(float*)a == *(float*)b;
-    case T_DOUBLE: return *(double*)a == *(double*)b;
-    }
-    return 0;
-}
-
-int cmp_lt(void *a, numerictype_t atag, void *b, numerictype_t btag)
-{
-    if (atag==btag)
-        return cmp_same_lt(a, b, atag);
-
-    double da = conv_to_double(a, atag);
-    double db = conv_to_double(b, btag);
-
-    // casting to double will only get the wrong answer for big int64s
-    // that differ in low bits
-    if (da < db)
-        return 1;
-    if (db < da)
-        return 0;
-
-    if (atag == T_UINT64) {
-        // this is safe because if a had been bigger than S64_MAX,
-        // we would already have concluded that it's bigger than b.
-        if (btag == T_INT64) {
-            return ((int64_t)*(uint64_t*)a < *(int64_t*)b);
-        }
-        else if (btag == T_DOUBLE) {
-            if (db != db) return 0;
-            return (*(uint64_t*)a < (uint64_t)*(double*)b);
-        }
-    }
-    else if (atag == T_INT64) {
-        if (btag == T_UINT64) {
-            return (*(int64_t*)a < (int64_t)*(uint64_t*)b);
-        }
-        else if (btag == T_DOUBLE) {
-            if (db != db) return 0;
-            return (*(int64_t*)a < (int64_t)*(double*)b);
-        }
-    }
-    else if (btag == T_UINT64) {
-        if (atag == T_INT64) {
-            return ((int64_t)*(uint64_t*)b > *(int64_t*)a);
-        }
-        else if (atag == T_DOUBLE) {
-            if (da != da) return 0;
-            return (*(uint64_t*)b > (uint64_t)*(double*)a);
-        }
-    }
-    else if (btag == T_INT64) {
-        if (atag == T_UINT64) {
-            return (*(int64_t*)b > (int64_t)*(uint64_t*)a);
-        }
-        else if (atag == T_DOUBLE) {
-            if (da != da) return 0;
-            return (*(int64_t*)b > (int64_t)*(double*)a);
-        }
-    }
-    return 0;
-}
-
-int cmp_eq(void *a, numerictype_t atag, void *b, numerictype_t btag,
-           int equalnans)
-{
-    if (atag==btag && (!equalnans || atag < T_FLOAT))
-        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 (equalnans) {
-            return *(uint64_t*)&da == *(uint64_t*)&db;
-        }
-        return (da == db);
-    }
-
-    if (da != db)
-        return 0;
-
-    if (atag == T_UINT64) {
-        // this is safe because if a had been bigger than S64_MAX,
-        // we would already have concluded that it's bigger than b.
-        if (btag == T_INT64) {
-            return ((int64_t)*(uint64_t*)a == *(int64_t*)b);
-        }
-        else if (btag == T_DOUBLE) {
-            return (*(uint64_t*)a == (uint64_t)(int64_t)*(double*)b);
-        }
-    }
-    else if (atag == T_INT64) {
-        if (btag == T_UINT64) {
-            return (*(int64_t*)a == (int64_t)*(uint64_t*)b);
-        }
-        else if (btag == T_DOUBLE) {
-            return (*(int64_t*)a == (int64_t)*(double*)b);
-        }
-    }
-    else if (btag == T_UINT64) {
-        if (atag == T_INT64) {
-            return ((int64_t)*(uint64_t*)b == *(int64_t*)a);
-        }
-        else if (atag == T_DOUBLE) {
-            return (*(uint64_t*)b == (uint64_t)(int64_t)*(double*)a);
-        }
-    }
-    else if (btag == T_INT64) {
-        if (atag == T_UINT64) {
-            return (*(int64_t*)b == (int64_t)*(uint64_t*)a);
-        }
-        else if (atag == T_DOUBLE) {
-            return (*(int64_t*)b == (int64_t)*(double*)a);
-        }
-    }
-    return 1;
-}
-
-#ifdef ENABLE_LLT_TEST
-void test_operators()
-{
-    int8_t i8, i8b;
-    uint8_t ui8, ui8b;
-    int16_t i16, i16b;
-    uint16_t ui16, ui16b;
-    int32_t i32, i32b;
-    uint32_t ui32, ui32b;
-    int64_t i64, i64b;
-    uint64_t ui64, ui64b;
-    float f, fb;
-    double d, db;
-
-    ui64 = U64_MAX;
-    ui64b = U64_MAX-1;
-    i64 = S64_MIN;
-    i64b = i64+1;
-    d = (double)ui64;
-    db = (double)i64b;
-
-    assert(cmp_lt(&i64, T_INT64, &ui64, T_UINT64));
-    assert(!cmp_lt(&ui64, T_UINT64, &i64, T_INT64));
-    assert(cmp_lt(&i64, T_INT64, &ui64b, T_UINT64));
-    assert(!cmp_lt(&ui64b, T_UINT64, &i64, T_INT64));
-    assert(cmp_lt(&i64, T_INT64, &i64b, T_INT64));
-    assert(!cmp_lt(&i64b, T_INT64, &i64, T_INT64));
-
-    // try to compare a double too big to fit in an int64 with an
-    // int64 requiring too much precision to fit in a double...
-    // this case fails but it's very difficult/expensive to support
-    //assert(cmp_lt(&ui64b, T_UINT64, &d, T_DOUBLE));
-
-    i64 = S64_MAX;
-    ui64 = S64_MAX-1;
-    assert(cmp_lt(&ui64, T_UINT64, &i64, T_INT64));
-    assert(!cmp_lt(&i64, T_INT64, &ui64, T_UINT64));
-    i64 = S64_MAX-1;
-    ui64 = S64_MAX;
-    assert(cmp_lt(&i64, T_INT64, &ui64, T_UINT64));
-    assert(!cmp_lt(&ui64, T_UINT64, &i64, T_INT64));
-
-    d = DBL_MAXINT;
-    i64 = DBL_MAXINT+100;
-    assert(cmp_lt(&d, T_DOUBLE, &i64, T_INT64));
-    assert(!cmp_lt(&i64, T_INT64, &d, T_DOUBLE));
-    i64 = DBL_MAXINT+10;
-    assert(cmp_lt(&d, T_DOUBLE, &i64, T_INT64));
-    assert(!cmp_lt(&i64, T_INT64, &d, T_DOUBLE));
-    i64 = DBL_MAXINT+1;
-    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, 0));
-    i64 = DBL_MAXINT;
-    assert(cmp_eq(&d, T_DOUBLE, &i64, T_INT64, 0));
-}
-#endif
--- a/llt/swapreverse.c
+++ /dev/null
@@ -1,187 +1,0 @@
-#include <stdlib.h>
-#include <string.h>
-#include <stddef.h>
-#include <alloca.h>
-#include "dtypes.h"
-#include "utils.h"
-
-void memswap(char *a, char *b, size_t sz)
-{
-    int8_t i8;
-    int32_t i32;
-    int32_t *a4, *b4;
-
-    if (sz < 4) {
-        while (sz--) {
-            i8 = *a;
-            *a++ = *b;
-            *b++ = i8;
-        }
-    }
-    else {
-        while (sz & 0x3) {
-            i8 = *a;
-            *a++ = *b;
-            *b++ = i8;
-            sz--;
-        }
-        a4 = (int32_t*)a;
-        b4 = (int32_t*)b;
-        sz >>= 2;
-        while (sz--) {
-            i32 = *a4;
-            *a4++ = *b4;
-            *b4++ = i32;
-        }
-    }
-}
-
-void memreverse(char *a, size_t n, size_t elsz)
-{
-    int64_t i64, *pi64;
-    int32_t i32, *pi32;
-    int16_t i16, *pi16;
-    int8_t i8;
-    size_t i;
-    char *temp;
-    size_t eli, tot;
-
-    if (n==0 || elsz==0) return;
-    switch(elsz) {
-    case 16:
-        pi64 = (int64_t*)a;
-        for(i=0; i < n/2; i++) {
-            i64 = pi64[2*i];
-            pi64[2*i] = pi64[2*(n-i-1)];
-            pi64[2*(n-i-1)] = i64;
-
-            i64 = pi64[2*i+1];
-            pi64[2*i+1] = pi64[2*(n-i-1)+1];
-            pi64[2*(n-i-1)+1] = i64;
-        }
-        break;
-    case 8:
-        pi64 = (int64_t*)a;
-        for(i=0; i < n/2; i++) {
-            i64 = pi64[i];
-            pi64[i] = pi64[n-i-1];
-            pi64[n-i-1] = i64;
-        }
-        break;
-    case 4:
-        pi32 = (int32_t*)a;
-        for(i=0; i < n/2; i++) {
-            i32 = pi32[i];
-            pi32[i] = pi32[n-i-1];
-            pi32[n-i-1] = i32;
-        }
-        break;
-    case 2:
-        pi16 = (int16_t*)a;
-        for(i=0; i < n/2; i++) {
-            i16 = pi16[i];
-            pi16[i] = pi16[n-i-1];
-            pi16[n-i-1] = i16;
-        }
-        break;
-    case 1:
-        for(i=0; i < n/2; i++) {
-            i8 = a[i];
-            a[i] = a[n-i-1];
-            a[n-i-1] = i8;
-        }
-        break;
-    default:
-        tot = n*elsz;
-        if (elsz < 4097)
-            temp = alloca(elsz);
-        else
-            temp = malloc(elsz);
-
-        if (temp != NULL) {
-            for(i=0, eli=0; i < n/2; i++, eli+=elsz) {
-                memcpy(temp, &a[eli], elsz);
-                memcpy(&a[eli], &a[tot-eli-elsz], elsz);
-                memcpy(&a[tot-eli-elsz], temp, elsz);
-            }
-
-            if (elsz >= 4097)
-                free(temp);
-        }
-        break;
-    }
-}
-
-void memreverse_to(char *dest, char *a, size_t n, size_t elsz)
-{
-    int64_t *pi64, *di64;
-    int32_t *pi32, *di32;
-    int16_t *pi16, *di16;
-    size_t i;
-    size_t eli, tot;
-    if (n==0 || elsz==0) return;
-    switch(elsz) {
-    case 16:
-        pi64 = (int64_t*)a;
-        di64 = (int64_t*)dest;
-        for(i=0; i < n/2; i++) {
-            di64[2*i] = pi64[2*(n-i-1)];
-            di64[2*(n-i-1)] = pi64[2*i];
-
-            di64[2*i+1] = pi64[2*(n-i-1)+1];
-            di64[2*(n-i-1)+1] = pi64[2*i+1];
-        }
-        if (n&0x1) {
-            di64[2*i] = pi64[2*i];
-            di64[2*i+1] = pi64[2*i+1];
-        }
-        break;
-    case 8:
-        pi64 = (int64_t*)a;
-        di64 = (int64_t*)dest;
-        for(i=0; i < n/2; i++) {
-            di64[i] = pi64[n-i-1];
-            di64[n-i-1] = pi64[i];
-        }
-        if (n&0x1)
-            di64[i] = pi64[i];
-        break;
-    case 4:
-        pi32 = (int32_t*)a;
-        di32 = (int32_t*)dest;
-        for(i=0; i < n/2; i++) {
-            di32[i] = pi32[n-i-1];
-            di32[n-i-1] = pi32[i];
-        }
-        if (n&0x1)
-            di32[i] = pi32[i];
-        break;
-    case 2:
-        pi16 = (int16_t*)a;
-        di16 = (int16_t*)dest;
-        for(i=0; i < n/2; i++) {
-            di16[i] = pi16[n-i-1];
-            di16[n-i-1] = pi16[i];
-        }
-        if (n&0x1)
-            di16[i] = pi16[i];
-        break;
-    case 1:
-        for(i=0; i < n/2; i++) {
-            dest[i] = a[n-i-1];
-            dest[n-i-1] = a[i];
-        }
-        if (n&0x1)
-            dest[i] = a[i];
-        break;
-    default:
-        tot = n*elsz;
-        for(i=0, eli=0; i < n/2; i++, eli+=elsz) {
-            memcpy(&dest[eli], &a[tot - eli - elsz], elsz);
-            memcpy(&dest[tot - eli - elsz], &a[eli], elsz);
-        }
-        if (n&0x1)
-            memcpy(&dest[eli], &a[eli], elsz);
-        break;
-    }
-}
--- a/llt/unittest.c
+++ /dev/null
@@ -1,142 +1,0 @@
-#include <stdlib.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <wchar.h>
-#include "llt.h"
-
-int main()
-{
-    llt_init();
-
-    test_dblprint();
-    test_operators();
-
-    /*
-    char *buf = malloc(20000);
-    char *buf2 = malloc(20000);
-    FILE *f = fopen("textread.m","rb");
-    int i=0;
-    while (!feof(f))
-      buf[i++] = fgetc(f);
-    buf[i-1] = '\0';
-    int len = i-1;
-    double t0 = clock_now();
-    int j=0;
-    for(i=0; i < 20000; i++) {
-        //j+=u8_charnum(buf,len);
-        u8_reverse(buf2, buf, len);
-    }
-    printf("textread took %.4f sec (%d)\n", clock_now()-t0, j);
-
-    FILE *f2 = fopen("u8.txt","rb");
-    i=0;
-    while (!feof(f2))
-      buf[i++] = fgetc(f2);
-    buf[i-1] = '\0';
-    len = i-1;
-    t0 = clock_now();
-    j=0;
-    for(i=0; i < 20000; i++) {
-        //j+=u8_charnum(buf,len);
-        u8_reverse(buf2, buf, len);
-    }
-    printf("u8 took %.4f sec (%d)\n\n", clock_now()-t0, j);
-    */
-
-    test_ios();
-
-    return 0;
-}
-
-static void prettycplx(double r, double i)
-{
-    char str[64];
-    snprint_cplx(str, sizeof(str), r, i, 0, 16, 3, 10, 1);
-    fputs(str, stdout);
-    fputc('\n', stdout);
-}
-
-static void prettyreal(double r)
-{
-    char str[64];
-    snprint_real(str, sizeof(str), r, 0, 16, 3, 10);
-    fputs(str, stdout);
-    fputc('\n', stdout);
-}
-
-void test_dblprint()
-{
-    char str[64];
-
-    prettycplx(0,0);
-    prettycplx(1,0);
-    prettycplx(0,1);
-    prettycplx(1,1);
-    prettycplx(-1,0);
-    prettycplx(0,-1);
-    prettycplx(1,-1);
-    prettycplx(-1,1);
-    prettycplx(-1,-1);
-    prettycplx(2,0);
-    prettycplx(0,2);
-    prettycplx(2,2);
-    prettycplx(-2,0);
-    prettycplx(0,-2);
-    prettycplx(2,-2);
-    prettycplx(-2,2);
-    prettycplx(-2,-2);
-
-    prettyreal(1.5);
-    prettyreal(1.1);
-    prettyreal(1.1e-100);
-    prettyreal(1.1e20);
-    prettyreal(123456789);
-    prettyreal(1234567890);
-    prettyreal(12345678901);
-    prettyreal(-12345678901);
-    prettyreal(12345678901223);
-    prettyreal(-12345678901223);
-    prettyreal(.02);
-    prettyreal(.002);
-    prettyreal(.0002);
-    prettyreal(-.0002);
-    prettyreal(.00002);
-    prettyreal(-.00002);
-
-    prettyreal(1.0/0);
-    prettyreal(-1.0/0);
-    prettyreal(strtod("nan",NULL));
-    prettyreal(0.0/0);
-    prettyreal(-0.0/0);
-
-    prettyreal(DBL_EPSILON);
-}
-
-void test_ios()
-{
-    ios_t *out = ios_stdout();
-    ios_t *in = ios_stdin();
-
-    ios_putc('a', out);
-    ios_putc('b', out);
-    ios_putc('\n', out);
-
-    char c[80];
-    size_t i=0;
-    ios_t sts;
-    ios_str(&sts, "Test string.");
-    c[i++] = ios_getc(&sts);
-    c[i++] = ios_getc(&sts);
-    c[i++] = ios_getc(&sts);
-    c[i++] = '\0';
-    printf("got: \"%s\"\n", c);
-
-    ios_t ms;
-    ios_mem(&ms, 10);
-    int j;
-    for(j=0; j < 16; j++)
-        ios_puts("passersby were amazed by the ", &ms);
-    size_t bs;
-    char *bigstr = ios_takebuf(&ms, &bs);
-    printf("got: \"%s\" (size %d)\n", bigstr, bs);
-}
--- a/llt/unittest.c.1
+++ /dev/null
@@ -1,81 +1,0 @@
-#include <stdlib.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <wchar.h>
-#include "llt.h"
-
-int main()
-{
-    llt_init();
-
-    test_dblprint();
-    test_operators();
-
-    return 0;
-}
-
-static void prettycplx(double r, double i)
-{
-    char str[64];
-    snprint_cplx(str, sizeof(str), r, i, 0, 16, 3, 10, 1);
-    fputs(str, stdout);
-    fputc('\n', stdout);
-}
-
-static void prettyreal(double r)
-{
-    char str[64];
-    snprint_real(str, sizeof(str), r, 0, 16, 3, 10);
-    fputs(str, stdout);
-    fputc('\n', stdout);
-}
-
-void test_dblprint()
-{
-    char str[64];
-
-    dbl_tolerance(1e-12);
-
-    prettycplx(0,0);
-    prettycplx(1,0);
-    prettycplx(0,1);
-    prettycplx(1,1);
-    prettycplx(-1,0);
-    prettycplx(0,-1);
-    prettycplx(1,-1);
-    prettycplx(-1,1);
-    prettycplx(-1,-1);
-    prettycplx(2,0);
-    prettycplx(0,2);
-    prettycplx(2,2);
-    prettycplx(-2,0);
-    prettycplx(0,-2);
-    prettycplx(2,-2);
-    prettycplx(-2,2);
-    prettycplx(-2,-2);
-
-    prettyreal(1.5);
-    prettyreal(1.1);
-    prettyreal(1.1e-100);
-    prettyreal(1.1e20);
-    prettyreal(123456789);
-    prettyreal(1234567890);
-    prettyreal(12345678901);
-    prettyreal(-12345678901);
-    prettyreal(12345678901223);
-    prettyreal(-12345678901223);
-    prettyreal(.02);
-    prettyreal(.002);
-    prettyreal(.0002);
-    prettyreal(-.0002);
-    prettyreal(.00002);
-    prettyreal(-.00002);
-
-    prettyreal(1.0/0);
-    prettyreal(-1.0/0);
-    prettyreal(strtod("nan",NULL));
-    prettyreal(0.0/0);
-    prettyreal(-0.0/0);
-
-    prettyreal(DBL_EPSILON);
-}
--- a/llt/utils.h
+++ b/llt/utils.h
@@ -1,78 +1,9 @@
 #ifndef __UTILS_H_
 #define __UTILS_H_
 
-/* these functions byteswap any-size units --------------------- */
-void bswap(byte_t *s, size_t n);
-void bswap_to(byte_t *dest, byte_t *src, size_t n);
-void bswap_buffer(byte_t *data, size_t sz, size_t npts);
-/* ------------------------------------------------------------- */
-
-/* reverse the order of elements of any size, in place or out of place */
-/* n is the number of elements */
-void memreverse(char *a, size_t n, size_t elsz);
-void memreverse_to(char *dest, char *a, size_t n, size_t elsz);
-
-/* swap the contents of two buffers */
-void memswap(char *a, char *b, size_t sz);
-
-/* allocating aligned blocks ----------------------------------- */
-void *malloc_aligned(size_t size, size_t align_size);
-void free_aligned(void *ptr);
-void *realloc_aligned(void *ptr, size_t size, size_t align_size);
-/* ------------------------------------------------------------- */
-
-int double_exponent(double d);
-double double_mantissa(double d);
-int float_exponent(float f);
-float float_mantissa(float f);
-void snprint_real(char *s, size_t cnt, double r,
-                  int width,    // printf field width, or 0
-                  int dec,      // # decimal digits desired, recommend 16
-                  // # of zeros in .00...0x before using scientific notation
-                  // recommend 3-4 or so
-                  int max_digs_rt,
-                  // # of digits left of decimal before scientific notation
-                  // recommend 10
-                  int max_digs_lf);
-void snprint_cplx(char *s, size_t cnt, double re, double im,
-                  // args to pass on to snprint_real
-                  int width, int dec,
-                  int max_digs_rt, int max_digs_lf,
-                  // print spaces around sign in a+bi
-                  int spflag);
-
 char *uint2str(char *dest, size_t len, uint64_t num, uint32_t base);
 int str2int(char *str, size_t len, int64_t *res, uint32_t base);
 int isdigit_base(char c, int base);
-
-extern double trunc(double x);
-
-STATIC_INLINE double fpart(double arg)
-{
-    return arg - trunc(arg);
-}
-
-#define ipart(x) trunc(x)
-
-numerictype_t effective_numerictype(double r);
-double conv_to_double(void *data, numerictype_t tag);
-void conv_from_double(void *data, double d, numerictype_t tag);
-int64_t conv_to_int64(void *data, numerictype_t tag);
-uint64_t conv_to_uint64(void *data, numerictype_t tag);
-int32_t conv_to_int32(void *data, numerictype_t tag);
-uint32_t conv_to_uint32(void *data, numerictype_t tag);
-#ifdef BITS64
-#define conv_to_long conv_to_int64
-#define conv_to_ulong conv_to_uint64
-#else
-#define conv_to_long conv_to_int32
-#define conv_to_ulong conv_to_uint32
-#endif
-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 equalnans);
 
 #ifdef ARCH_X86_64
 #  define LEGACY_REGS "=Q"
--- /dev/null
+++ b/operators.c
@@ -1,0 +1,362 @@
+#include <limits.h>
+#include <assert.h>
+#include "dtypes.h"
+#include "utils.h"
+#include "ieee754.h"
+
+extern double trunc(double x);
+
+STATIC_INLINE double fpart(double arg)
+{
+    return arg - trunc(arg);
+}
+
+// given a number, determine an appropriate type for storing it
+#if 0
+numerictype_t effective_numerictype(double r)
+{
+    double fp;
+
+    fp = fpart(r);
+    if (fp != 0 || r > U64_MAX || r < S64_MIN) {
+        if (r > FLT_MAX || r < -FLT_MAX || (fabs(r) < FLT_MIN)) {
+            return T_DOUBLE;
+        }
+        else {
+            return T_FLOAT;
+        }
+    }
+    else if (r >= SCHAR_MIN && r <= SCHAR_MAX) {
+        return T_INT8;
+    }
+    else if (r >= SHRT_MIN && r <= SHRT_MAX) {
+        return T_INT16;
+    }
+    else if (r >= INT_MIN && r <= INT_MAX) {
+        return T_INT32;
+    }
+    else if (r <= S64_MAX) {
+        return T_INT64;
+    }
+    return T_UINT64;
+}
+#else
+// simpler version implementing a smaller preferred type repertoire
+numerictype_t effective_numerictype(double r)
+{
+    double fp;
+
+    fp = fpart(r);
+    if (fp != 0 || r > U64_MAX || r < S64_MIN) {
+        return T_DOUBLE;
+    }
+    else if (r >= INT_MIN && r <= INT_MAX) {
+        return T_INT32;
+    }
+    else if (r <= S64_MAX) {
+        return T_INT64;
+    }
+    return T_UINT64;
+}
+#endif
+
+double conv_to_double(void *data, numerictype_t tag)
+{
+    double d=0;
+    switch (tag) {
+    case T_INT8:   d = (double)*(int8_t*)data; break;
+    case T_UINT8:  d = (double)*(uint8_t*)data; break;
+    case T_INT16:  d = (double)*(int16_t*)data; break;
+    case T_UINT16: d = (double)*(uint16_t*)data; break;
+    case T_INT32:  d = (double)*(int32_t*)data; break;
+    case T_UINT32: d = (double)*(uint32_t*)data; break;
+    case T_INT64:
+        d = (double)*(int64_t*)data;
+        if (d > 0 && *(int64_t*)data < 0)  // can happen!
+            d = -d;
+        break;
+    case T_UINT64: d = (double)*(uint64_t*)data; break;
+    case T_FLOAT:  d = (double)*(float*)data; break;
+    case T_DOUBLE: return *(double*)data;
+    }
+    return d;
+}
+
+void conv_from_double(void *dest, double d, numerictype_t tag)
+{
+    switch (tag) {
+    case T_INT8:   *(int8_t*)dest = d; break;
+    case T_UINT8:  *(uint8_t*)dest = d; break;
+    case T_INT16:  *(int16_t*)dest = d; break;
+    case T_UINT16: *(uint16_t*)dest = d; break;
+    case T_INT32:  *(int32_t*)dest = d; break;
+    case T_UINT32: *(uint32_t*)dest = d; break;
+    case T_INT64:
+        *(int64_t*)dest = d;
+        if (d > 0 && *(int64_t*)dest < 0)  // 0x8000000000000000 is a bitch
+            *(int64_t*)dest = S64_MAX;
+        break;
+    case T_UINT64: *(uint64_t*)dest = (int64_t)d; break;
+    case T_FLOAT:  *(float*)dest = d; break;
+    case T_DOUBLE: *(double*)dest = d; break;
+    }
+}
+
+#define CONV_TO_INTTYPE(type)                               \
+type##_t conv_to_##type(void *data, numerictype_t tag)      \
+{                                                           \
+    type##_t i=0;                                           \
+    switch (tag) {                                          \
+    case T_INT8:   i = (type##_t)*(int8_t*)data; break;     \
+    case T_UINT8:  i = (type##_t)*(uint8_t*)data; break;    \
+    case T_INT16:  i = (type##_t)*(int16_t*)data; break;    \
+    case T_UINT16: i = (type##_t)*(uint16_t*)data; break;   \
+    case T_INT32:  i = (type##_t)*(int32_t*)data; break;    \
+    case T_UINT32: i = (type##_t)*(uint32_t*)data; break;   \
+    case T_INT64:  i = (type##_t)*(int64_t*)data; break;    \
+    case T_UINT64: i = (type##_t)*(uint64_t*)data; break;   \
+    case T_FLOAT:  i = (type##_t)*(float*)data; break;      \
+    case T_DOUBLE: i = (type##_t)*(double*)data; break;     \
+    }                                                       \
+    return i;                                               \
+}
+
+CONV_TO_INTTYPE(int64)
+CONV_TO_INTTYPE(int32)
+CONV_TO_INTTYPE(uint32)
+
+// this is needed to work around a possible compiler bug
+// casting negative floats and doubles to uint64. you need
+// to cast to int64 first.
+uint64_t conv_to_uint64(void *data, numerictype_t tag)
+{
+    uint64_t i=0;
+    switch (tag) {
+    case T_INT8:   i = (uint64_t)*(int8_t*)data; break;
+    case T_UINT8:  i = (uint64_t)*(uint8_t*)data; break;
+    case T_INT16:  i = (uint64_t)*(int16_t*)data; break;
+    case T_UINT16: i = (uint64_t)*(uint16_t*)data; break;
+    case T_INT32:  i = (uint64_t)*(int32_t*)data; break;
+    case T_UINT32: i = (uint64_t)*(uint32_t*)data; break;
+    case T_INT64:  i = (uint64_t)*(int64_t*)data; break;
+    case T_UINT64: i = (uint64_t)*(uint64_t*)data; break;
+    case T_FLOAT:
+        if (*(float*)data >= 0)
+            i = (uint64_t)*(float*)data;
+        else
+            i = (uint64_t)(int64_t)*(float*)data;
+        break;
+    case T_DOUBLE:
+        if (*(double*)data >= 0)
+            i = (uint64_t)*(double*)data;
+        else
+            i = (uint64_t)(int64_t)*(double*)data;
+        break;
+    }
+    return i;
+}
+
+int cmp_same_lt(void *a, void *b, numerictype_t tag)
+{
+    switch (tag) {
+    case T_INT8:   return *(int8_t*)a < *(int8_t*)b;
+    case T_UINT8:  return *(uint8_t*)a < *(uint8_t*)b;
+    case T_INT16:  return *(int16_t*)a < *(int16_t*)b;
+    case T_UINT16: return *(uint16_t*)a < *(uint16_t*)b;
+    case T_INT32:  return *(int32_t*)a < *(int32_t*)b;
+    case T_UINT32: return *(uint32_t*)a < *(uint32_t*)b;
+    case T_INT64:  return *(int64_t*)a < *(int64_t*)b;
+    case T_UINT64: return *(uint64_t*)a < *(uint64_t*)b;
+    case T_FLOAT:  return *(float*)a < *(float*)b;
+    case T_DOUBLE: return *(double*)a < *(double*)b;
+    }
+    return 0;
+}
+
+int cmp_same_eq(void *a, void *b, numerictype_t tag)
+{
+    switch (tag) {
+    case T_INT8:   return *(int8_t*)a == *(int8_t*)b;
+    case T_UINT8:  return *(uint8_t*)a == *(uint8_t*)b;
+    case T_INT16:  return *(int16_t*)a == *(int16_t*)b;
+    case T_UINT16: return *(uint16_t*)a == *(uint16_t*)b;
+    case T_INT32:  return *(int32_t*)a == *(int32_t*)b;
+    case T_UINT32: return *(uint32_t*)a == *(uint32_t*)b;
+    case T_INT64:  return *(int64_t*)a == *(int64_t*)b;
+    case T_UINT64: return *(uint64_t*)a == *(uint64_t*)b;
+    case T_FLOAT:  return *(float*)a == *(float*)b;
+    case T_DOUBLE: return *(double*)a == *(double*)b;
+    }
+    return 0;
+}
+
+int cmp_lt(void *a, numerictype_t atag, void *b, numerictype_t btag)
+{
+    if (atag==btag)
+        return cmp_same_lt(a, b, atag);
+
+    double da = conv_to_double(a, atag);
+    double db = conv_to_double(b, btag);
+
+    // casting to double will only get the wrong answer for big int64s
+    // that differ in low bits
+    if (da < db)
+        return 1;
+    if (db < da)
+        return 0;
+
+    if (atag == T_UINT64) {
+        // this is safe because if a had been bigger than S64_MAX,
+        // we would already have concluded that it's bigger than b.
+        if (btag == T_INT64) {
+            return ((int64_t)*(uint64_t*)a < *(int64_t*)b);
+        }
+        else if (btag == T_DOUBLE) {
+            if (db != db) return 0;
+            return (*(uint64_t*)a < (uint64_t)*(double*)b);
+        }
+    }
+    else if (atag == T_INT64) {
+        if (btag == T_UINT64) {
+            return (*(int64_t*)a < (int64_t)*(uint64_t*)b);
+        }
+        else if (btag == T_DOUBLE) {
+            if (db != db) return 0;
+            return (*(int64_t*)a < (int64_t)*(double*)b);
+        }
+    }
+    else if (btag == T_UINT64) {
+        if (atag == T_INT64) {
+            return ((int64_t)*(uint64_t*)b > *(int64_t*)a);
+        }
+        else if (atag == T_DOUBLE) {
+            if (da != da) return 0;
+            return (*(uint64_t*)b > (uint64_t)*(double*)a);
+        }
+    }
+    else if (btag == T_INT64) {
+        if (atag == T_UINT64) {
+            return (*(int64_t*)b > (int64_t)*(uint64_t*)a);
+        }
+        else if (atag == T_DOUBLE) {
+            if (da != da) return 0;
+            return (*(int64_t*)b > (int64_t)*(double*)a);
+        }
+    }
+    return 0;
+}
+
+int cmp_eq(void *a, numerictype_t atag, void *b, numerictype_t btag,
+           int equalnans)
+{
+    if (atag==btag && (!equalnans || atag < T_FLOAT))
+        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 (equalnans) {
+            return *(uint64_t*)&da == *(uint64_t*)&db;
+        }
+        return (da == db);
+    }
+
+    if (da != db)
+        return 0;
+
+    if (atag == T_UINT64) {
+        // this is safe because if a had been bigger than S64_MAX,
+        // we would already have concluded that it's bigger than b.
+        if (btag == T_INT64) {
+            return ((int64_t)*(uint64_t*)a == *(int64_t*)b);
+        }
+        else if (btag == T_DOUBLE) {
+            return (*(uint64_t*)a == (uint64_t)(int64_t)*(double*)b);
+        }
+    }
+    else if (atag == T_INT64) {
+        if (btag == T_UINT64) {
+            return (*(int64_t*)a == (int64_t)*(uint64_t*)b);
+        }
+        else if (btag == T_DOUBLE) {
+            return (*(int64_t*)a == (int64_t)*(double*)b);
+        }
+    }
+    else if (btag == T_UINT64) {
+        if (atag == T_INT64) {
+            return ((int64_t)*(uint64_t*)b == *(int64_t*)a);
+        }
+        else if (atag == T_DOUBLE) {
+            return (*(uint64_t*)b == (uint64_t)(int64_t)*(double*)a);
+        }
+    }
+    else if (btag == T_INT64) {
+        if (atag == T_UINT64) {
+            return (*(int64_t*)b == (int64_t)*(uint64_t*)a);
+        }
+        else if (atag == T_DOUBLE) {
+            return (*(int64_t*)b == (int64_t)*(double*)a);
+        }
+    }
+    return 1;
+}
+
+#ifdef ENABLE_LLT_TEST
+void test_operators()
+{
+    int8_t i8, i8b;
+    uint8_t ui8, ui8b;
+    int16_t i16, i16b;
+    uint16_t ui16, ui16b;
+    int32_t i32, i32b;
+    uint32_t ui32, ui32b;
+    int64_t i64, i64b;
+    uint64_t ui64, ui64b;
+    float f, fb;
+    double d, db;
+
+    ui64 = U64_MAX;
+    ui64b = U64_MAX-1;
+    i64 = S64_MIN;
+    i64b = i64+1;
+    d = (double)ui64;
+    db = (double)i64b;
+
+    assert(cmp_lt(&i64, T_INT64, &ui64, T_UINT64));
+    assert(!cmp_lt(&ui64, T_UINT64, &i64, T_INT64));
+    assert(cmp_lt(&i64, T_INT64, &ui64b, T_UINT64));
+    assert(!cmp_lt(&ui64b, T_UINT64, &i64, T_INT64));
+    assert(cmp_lt(&i64, T_INT64, &i64b, T_INT64));
+    assert(!cmp_lt(&i64b, T_INT64, &i64, T_INT64));
+
+    // try to compare a double too big to fit in an int64 with an
+    // int64 requiring too much precision to fit in a double...
+    // this case fails but it's very difficult/expensive to support
+    //assert(cmp_lt(&ui64b, T_UINT64, &d, T_DOUBLE));
+
+    i64 = S64_MAX;
+    ui64 = S64_MAX-1;
+    assert(cmp_lt(&ui64, T_UINT64, &i64, T_INT64));
+    assert(!cmp_lt(&i64, T_INT64, &ui64, T_UINT64));
+    i64 = S64_MAX-1;
+    ui64 = S64_MAX;
+    assert(cmp_lt(&i64, T_INT64, &ui64, T_UINT64));
+    assert(!cmp_lt(&ui64, T_UINT64, &i64, T_INT64));
+
+    d = DBL_MAXINT;
+    i64 = DBL_MAXINT+100;
+    assert(cmp_lt(&d, T_DOUBLE, &i64, T_INT64));
+    assert(!cmp_lt(&i64, T_INT64, &d, T_DOUBLE));
+    i64 = DBL_MAXINT+10;
+    assert(cmp_lt(&d, T_DOUBLE, &i64, T_INT64));
+    assert(!cmp_lt(&i64, T_INT64, &d, T_DOUBLE));
+    i64 = DBL_MAXINT+1;
+    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, 0));
+    i64 = DBL_MAXINT;
+    assert(cmp_eq(&d, T_DOUBLE, &i64, T_INT64, 0));
+}
+#endif
--- a/print.c
+++ b/print.c
@@ -1,3 +1,5 @@
+#include "ieee754.h"
+
 extern void *memrchr(const void *s, int c, size_t n);
 
 static htable_t printconses;
@@ -516,6 +518,93 @@
         }
     }
     outc('"', f);
+}
+
+int double_exponent(double d)
+{
+    union ieee754_double dl;
+
+    dl.d = d;
+    return dl.ieee.exponent - IEEE754_DOUBLE_BIAS;
+}
+
+void snprint_real(char *s, size_t cnt, double r,
+                  int width,    // printf field width, or 0
+                  int dec,      // # decimal digits desired, recommend 16
+                  // # of zeros in .00...0x before using scientific notation
+                  // recommend 3-4 or so
+                  int max_digs_rt,
+                  // # of digits left of decimal before scientific notation
+                  // recommend 10
+                  int max_digs_lf)
+{
+    int mag;
+    double fpart, temp;
+    char format[8];
+    char num_format[3];
+    int sz, keepz=0;
+
+    s[0] = '\0';
+    if (width == -1) {
+        width = 0;
+        keepz=1;
+    }
+    if (isnan(r)) {
+        if (sign_bit(r))
+            strncpy(s, "-nan", cnt);
+        else
+            strncpy(s, "nan", cnt);
+        return;
+    }
+    if (r == 0) {
+        strncpy(s, "0", cnt);
+        return;
+    }
+
+    num_format[0] = 'l';
+    num_format[2] = '\0';
+
+    mag = double_exponent(r);
+
+    mag = (int)(((double)mag)/LOG2_10 + 0.5);
+    if (r == 0)
+        mag = 0;
+    if ((mag > max_digs_lf-1) || (mag < -max_digs_rt)) {
+        num_format[1] = 'e';
+        temp = r/pow(10, mag);      /* see if number will have a decimal */
+        fpart = temp - floor(temp); /* when written in scientific notation */
+    }
+    else {
+        num_format[1] = 'f';
+        fpart = r - floor(r);
+    }
+    if (fpart == 0)
+        dec = 0;
+    if (width == 0) {
+        snprintf(format, 8, "%%.%d%s", dec, num_format);
+    }
+    else {
+        snprintf(format, 8, "%%%d.%d%s", width, dec, num_format);
+    }
+    sz = snprintf(s, cnt, format, r);
+    /* trim trailing zeros from fractions. not when using scientific
+       notation, since we might have e.g. 1.2000e+100. also not when we
+       need a specific output width */
+    if (width == 0 && !keepz) {
+        if (sz > 2 && fpart && num_format[1]!='e') {
+            while (s[sz-1] == '0') {
+                s[sz-1]='\0';
+                sz--;
+            }
+            // don't need trailing .
+            if (s[sz-1] == '.') {
+                s[sz-1] = '\0';
+                sz--;
+            }
+        }
+    }
+    // TODO. currently 1.1e20 prints as 1.1000000000000000e+20; be able to
+    // get rid of all those zeros.
 }
 
 static numerictype_t sym_to_numtype(value_t type);