scsh-0.5/vm/arith.scm

118 lines
3.7 KiB
Scheme

; Arithmetic that checks for overflow
(define (carefully op)
(lambda (x y succ fail)
(let ((z (op (extract-fixnum x) (extract-fixnum y))))
(if (or (too-big-for-fixnum? z)
(too-small-for-fixnum? z))
(goto fail x y)
(goto succ (enter-fixnum z))))))
(define add-carefully (carefully +))
(define subtract-carefully (carefully -))
(define half-word-size (quotient bits-per-cell 2))
(define half-word-mask (- (ashl 1 half-word-size) 1))
(define max-middle (ashl 1 (- (- bits-per-fixnum 1) half-word-size)))
(define (multiply-carefully x y succ fail)
(let* ((a (extract-fixnum x))
(b (extract-fixnum y))
(positive-result? (if (>= a 0)
(>= b 0)
(< b 0)))
(a (abs a))
(b (abs b))
(lo-a (bitwise-and half-word-mask a))
(lo-b (bitwise-and half-word-mask b))
(hi-a (bitwise-and half-word-mask (high-bits a half-word-size)))
(hi-b (bitwise-and half-word-mask (high-bits b half-word-size)))
(lo-c (small* lo-a lo-b))
(mid-c (+ (small* lo-a hi-b) (small* lo-b hi-a)))
(c (+ lo-c (ashl mid-c half-word-size))))
(cond ((or (and (> hi-a 0) (> hi-b 0))
(too-big-for-fixnum? lo-c)
(> 0 lo-c)
(> mid-c max-middle))
(goto fail x y))
(positive-result?
(if (too-big-for-fixnum? c)
(goto fail x y)
(goto succ (enter-fixnum c))))
(else
(if (too-small-for-fixnum? (- 0 c))
(goto fail x y)
(goto succ (enter-fixnum (- 0 c))))))))
; Test cases for bits-per-cell = 28, bits-per-fixnum = 26
; (do ((i 2 (* i 2))
; (j (* -2 (expt 2 23)) (/ j 2)))
; ((>= j 0) 'ok)
; (write `((* ,i ,j) ?=? ,(* i j)))
; (newline))
(define (divide-carefully x y succ fail)
(quotient-carefully x y
(lambda (q)
(remainder-carefully x y
(lambda (r)
(if (= r (enter-fixnum 0))
(goto succ q)
(goto fail x y)))
fail))
fail))
; Watch out for (quotient least-fixnum -1)
(define (quotient-carefully x y succ fail)
(if (= y (enter-fixnum 0))
(fail x y)
(let* ((a (extract-fixnum x))
(b (extract-fixnum y))
(positive-result? (if (>= a 0)
(>= b 0)
(< b 0)))
(a (abs a))
(b (abs b))
(c (quotient a b)))
(cond ((not positive-result?)
(goto succ (enter-fixnum (- 0 c))))
((too-big-for-fixnum? c) ; (quotient least-fixnum -1)
(goto fail x y))
(else
(goto succ (enter-fixnum c)))))))
; Overflow check not necessary
(define (remainder-carefully x y succ fail)
(if (= y (enter-fixnum 0))
(goto fail x y)
(let* ((a (extract-fixnum x))
(b (extract-fixnum y))
(positive-result? (>= a 0))
(a (abs a))
(b (abs b))
(c (remainder a b)))
(goto succ (enter-fixnum (if positive-result? c (- 0 c)))))))
(define (shift-carefully value+tag count+tag succ fail)
(let ((value (extract-fixnum value+tag))
(count (extract-fixnum count+tag)))
(if (< count 0)
(goto succ (enter-fixnum (ashr value (- 0 count))))
(let ((result (extract-fixnum (enter-fixnum (ashl value count)))))
(if (and (= value (ashr result count))
(if (>= value 0)
(>= result 0)
(< result 0)))
(goto succ (enter-fixnum result))
(goto fail value+tag count+tag))))))
; beware of (abs least-fixnum)
(define (abs-carefully n succ fail)
(let ((r (abs n)))
(if (too-big-for-fixnum? r)
(goto fail n)
(goto succ (enter-fixnum r)))))