scsh-0.5/rts/floatnum.scm

297 lines
8.4 KiB
Scheme
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

; Copyright (c) 1993, 1994 Richard Kelsey and Jonathan Rees. See file COPYING.
; Inexact rational arithmetic using hacked-in floating point numbers.
(define-extended-number-type :floatnum (:rational)
(make-floatnum datum)
floatnum?
(datum floatnum-datum))
(define (make-float-datum) (make-code-vector 8 0))
(define-enumeration flop
(+ - * / = <
fixnum->float
string->float
float->string
exp log sin cos tan asin acos atan sqrt
floor
integer?
float->fixnum
quotient
remainder))
; Floating point at interrupt level? Naw!
; Actually, if floatnum-datum is open-coded, there won't be any
; opportunity to get an interrupt in any of the situations where
; floperate is used.
(define float-vec (make-vector 3 #f))
(define-syntax floperate
(syntax-rules ()
((floperate ?which ?x)
(vm-extension (+ ?which 100) ?x))
((floperate ?which ?x ?y)
(vm-extension (+ ?which 100) (cons ?x ?y)))
((floperate ?which ?x ?y ?z)
(begin (vector-set! float-vec 0 ?x)
(vector-set! float-vec 1 ?y)
(vector-set! float-vec 2 ?z)
(vm-extension (+ ?which 100) float-vec)))))
(define (float&float->float op)
(lambda (a b)
(let ((float1 (x->float a))
(float2 (x->float b))
(res (make-float-datum)))
(floperate op
(floatnum-datum float1)
(floatnum-datum float2)
res)
(make-floatnum res))))
(define (float&float->boolean op)
(lambda (a b)
(let ((float1 (x->float a))
(float2 (x->float b)))
(floperate op
(floatnum-datum float1)
(floatnum-datum float2)))))
(define (float1 op)
(lambda (float)
(floperate op (floatnum-datum float))))
(define (float->float op)
(lambda (a)
(let ((float (x->float a))
(res (make-float-datum)))
(floperate op (floatnum-datum float) res)
(make-floatnum res))))
(define (string->float string)
(let ((res (make-float-datum)))
(floperate (enum flop string->float) string res)
(make-floatnum res)))
(define (float->string float)
(let* ((res (make-string 40 #\space))
(len (floperate (enum flop float->string)
(floatnum-datum float)
res))
(str (substring res 0 len)))
(let loop ((i 0))
(cond ((>= i (string-length str))
(string-append str "."))
((or (char=? (string-ref str i) #\e)
(char=? (string-ref str i) #\.))
str)
(else
(loop (+ i 1)))))))
(define (x->float x)
(cond ((floatnum? x) x)
((integer? x)
(exact-integer->float (if (exact? x)
x
(inexact->exact x))))
((rational? x)
;; This loses when num or den overflows flonum range
;; but x doesn't.
(float/ (numerator x) (denominator x)))
(else
(error "cannot coerce to a float" x))))
; Conversion to/from exact integer
(define (exact-integer->float k)
(or (fixnum->float k)
(float+ (float* (fixnum->float definitely-a-fixnum)
(quotient k definitely-a-fixnum))
(fixnum->float (remainder k definitely-a-fixnum)))))
(define (fixnum->float k) ;Returns #f is k is a bignum
(let ((res (make-float-datum)))
(if (floperate (enum flop fixnum->float) k res)
(make-floatnum res)
#f)))
(define (float->exact-integer x)
(or (float->fixnum x)
(let ((d (fixnum->float definitely-a-fixnum)))
(+ (* definitely-a-fixnum
(float->exact-integer (float-quotient x d)))
(float->fixnum (float-remainder x d))))))
(define definitely-a-fixnum (expt 2 23)) ;Be conservative
(define integral-floatnum? (float1 (enum flop integer?)))
(define float->fixnum (float1 (enum flop float->fixnum)))
(define float+ (float&float->float (enum flop +)))
(define float- (float&float->float (enum flop -)))
(define float* (float&float->float (enum flop *)))
(define float/ (float&float->float (enum flop /)))
(define float-quotient (float&float->float (enum flop quotient)))
(define float-remainder (float&float->float (enum flop remainder)))
(define float-atan (float&float->float (enum flop atan)))
(define float= (float&float->boolean (enum flop =)))
(define float< (float&float->boolean (enum flop <)))
(define float-exp (float->float (enum flop exp)))
(define float-log (float->float (enum flop log)))
(define float-sin (float->float (enum flop sin)))
(define float-cos (float->float (enum flop cos)))
(define float-tan (float->float (enum flop tan)))
(define float-asin (float->float (enum flop asin)))
(define float-acos (float->float (enum flop acos)))
(define float-sqrt (float->float (enum flop sqrt)))
(define float-floor (float->float (enum flop floor)))
; This lets you do ,open floatnum to get faster invocation
(begin
(define exp float-exp)
(define log float-log)
(define sin float-sin)
(define cos float-cos)
(define tan float-tan)
(define asin float-asin)
(define acos float-acos)
(define atan float-atan)
(define sqrt float-sqrt))
(define (float-fraction-length x)
(let ((two (exact-integer->float 2)))
(do ((x x (float* x two))
(i 0 (+ i 1)))
((integral-floatnum? x) i)
(if (> i 1000) (error "I'm bored." x)))))
(define (float-denominator x)
(expt (exact-integer->float 2) (float-fraction-length x)))
(define (float-numerator x)
(float* x (float-denominator x)))
(define (float->exact x)
(if (integral-floatnum? x)
(float->exact-integer x) ;+++
(let ((lose (lambda ()
(call-error "no exact representation"
inexact->exact x)))
(q (expt 2 (float-fraction-length x))))
(if (exact? q)
(let ((e (/ (float->exact-integer
(float* x (exact-integer->float q)))
q)))
(if (exact? e)
e
(lose)))
(lose)))))
; Methods on floatnums
(define-method &integer? ((x :floatnum))
(integral-floatnum? x))
(define-method &rational? ((n :floatnum)) #t)
(define-method &exact? ((x :floatnum)) #f)
(define-method &inexact->exact ((x :floatnum))
(float->exact x))
(define-method &exact->inexact ((x :rational))
(x->float x)) ;Should do this only if the number is within range.
(define-method &floor ((x :floatnum)) (float-floor x))
; beware infinite regress
(define-method &numerator ((x :floatnum)) (float-numerator x))
(define-method &denominator ((x :floatnum)) (float-denominator x))
(define (define-floatnum-method mtable proc)
(define-method mtable ((m :rational) (n :rational)) (proc m n)))
(define-floatnum-method &+ float+)
(define-floatnum-method &- float-)
(define-floatnum-method &* float*)
(define-floatnum-method &/ float/)
(define-floatnum-method &quotient float-quotient)
(define-floatnum-method &remainder float-remainder)
(define-floatnum-method &= float=)
(define-floatnum-method &< float<)
(define-method &numerator ((x :rational)) (float-numerator x))
(define-method &denominator ((x :rational)) (float-denominator x))
(define-method &exp ((x :rational)) (float-exp x))
(define-method &log ((x :rational)) (float-log x))
(define-method &sqrt ((x :rational)) (float-sqrt x))
(define-method &sin ((x :rational)) (float-sin x))
(define-method &cos ((x :rational)) (float-cos x))
(define-method &tan ((x :rational)) (float-tan x))
(define-method &acos ((x :rational)) (float-acos x))
(define-floatnum-method &atan float-atan)
(define-method &number->string ((n :floatnum) radix)
(if (= radix 10)
(float->string n)
(next-method)))
; Oog.
(define (float-string? s)
(let ((len (string-length s)))
(define (start)
(cond ((< len 2)
#f)
((char-numeric? (string-ref s 0))
(digits 1 #f #f))
((and (or (char=? (string-ref s 0) #\+)
(char=? (string-ref s 0) #\-))
(char-numeric? (string-ref s 1)))
(digits 2 #f #f))
((and (char=? (string-ref s 0) #\.)
(char-numeric? (string-ref s 1)))
(digits 2 #t #f))
(else #f)))
(define (digits i dot? e?)
(cond ((>= i len) dot?)
((char-numeric? (string-ref s i))
(digits (+ i 1) dot? e?))
((and (char=? (string-ref s i) #\e)
(not e?))
(exponent (+ i 1)))
((and (char=? (string-ref s i) #\.)
(not dot?))
(digits (+ i 1) #t #f))
(else #f)))
(define (exponent i)
(cond ((>= i len) #f)
((char-numeric? (string-ref s i))
(digits (+ i 1) #t #t))
((or (char=? (string-ref s i) #\+)
(char=? (string-ref s i) #\-))
(exponent2 (+ i 1)))
(else #f)))
(define (exponent2 i)
(cond ((>= i len) #f)
((char-numeric? (string-ref s i))
(digits (+ i 1) #t #t))
(else #f)))
(start)))
(define-simple-type :float-string (:string) float-string?)
(define-method &really-string->number ((s :float-string) radix exact?)
(if (and (= radix 10)
(not exact?))
(string->float s)
(next-method)))