changing floating point number input.. (was realroots...)
Subject: changing floating point number input.. (was realroots...)
From: Richard Fateman
Date: Sat, 26 Aug 2006 17:55:36 -0700
oops. I didn't define tomaxrat. Fortunately it is already defined with
another name, cl-rat-to-maxima..
so the function is more self contained as show below.
To summarize:
On input, any floating point format can be used. If the floating point
value can be sensibly treated as a machine double float, it is made into a
machine double float. If it cannot, it is made into a bigfloat. The
judgment is: not too big a (pos or neg) exponent. not too small a (pos or
neg) exponent. not too many digits (more than 16 decimals).
Effects: if you put too many digits in a number like
1.234567890123456789012345e0, then you will get a bigfloat. If your program
doesn't work with bigfloats, this might be a problem.
if you put too large an exponent, e.g. 1.234e600, instead of getting an
error, you get a bigfloat.
RJF
;; new version by rjf, aug 26 2006
(defun make-number (indata)
;; check first for easy case
(if (null(cdr indata)) ; just an integer
(return-from make-number (readlist (car indata))))
(let* ((data (nreverse indata))
(left (nth 0 data))
(point (nth 1 data))
(right (nth 2 data))
(exponent-marker (car (nth 3 data)))
(exponent-sign (nth 4 data))
(exponent-value(nth 5 data)))
;; say the number is 123.45e6
(setf left (if (null left) 0 (readlist left))) ;; convert (#\1 #\2 #\3)
to 123
(setf right (if (null right)0 (readlist right)));; convert (#\4 #\5) to
45
(setf exponent-sign (cond ((null exponent-sign) 1) ;; either -1 or 1 for
exponent sign
((char= (car exponent-sign) #\-) -1)
(t 1)))
(setf exponent-value (if (null exponent-value)0 (readlist
exponent-value))); convert exponent
;;Maxima was set up to read in any number as a
;; double-float. though commercial macsyma distinguishes between
;; single and double. In this program we compute the exact
;; rational value of a decimal number which is entered in a
;; floating point format. We ask later if the original exponent
;; marker, E, D, S, e, d should be believed. That is, can the
;; number be represented accurately as a double. If not, we
;; convert it to a bigfloat. We may bump up fpprec, the number of
;; binary bits needed to convert the number correctly, if
;; necessary. (3.32 is bits per decimal digit..)
(let* ((fpprec (max fpprec (round(+ (* 3.32 (+(length (nth 0
data))(length (nth 2 data))))
(abs exponent-value)))))
(therational
(* (+ left
(if (= right 0) 0
(* right
(expt 10. (- (length (third data)))))))
(if (= exponent-value 0) 1
(expt 10. (* exponent-value exponent-sign)))))
)
;; at this point we can check the exponent marker and see what makes
sense.
(case exponent-marker
(#\B ($bfloat (cl-rat-to-maxima therational))) ;it was a bigfloat
marker. do it.
(t ;; (#\E #\D #\e #\d nil) one of the others.
;; now check that the number fits within double range.
;; and that the reader presented no more than 16 decimal digits.
;; and that it is not going to underflow to 0.
(if (and (< -2 (/ therational #.(expt 2 1023)) 2)
(>= 16 (+ (length (nth 0 data))(length (nth 2 data)) ))
(not (< -1 (* therational #.(expt 2 1023)) 1)) )
(* 1.0d0 therational) ; convert to double.
($bfloat (cl-rat-to-maxima therational)))))))) ;; otherwise, use
bigfloat.