oct-format.lisp 4.61 KB
Newer Older
Raymond Toy's avatar
Raymond Toy committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
;;;; -*- Mode: lisp -*-
;;;;
;;;; Copyright (c) 2007 Raymond Toy
;;;;
;;;; Permission is hereby granted, free of charge, to any person
;;;; obtaining a copy of this software and associated documentation
;;;; files (the "Software"), to deal in the Software without
;;;; restriction, including without limitation the rights to use,
;;;; copy, modify, merge, publish, distribute, sublicense, and/or sell
;;;; copies of the Software, and to permit persons to whom the
;;;; Software is furnished to do so, subject to the following
;;;; conditions:
;;;;
;;;; The above copyright notice and this permission notice shall be
;;;; included in all copies or substantial portions of the Software.
;;;;
;;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
;;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
;;;; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
;;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
;;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
;;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
;;;; OTHER DEALINGS IN THE SOFTWARE.

Raymond Toy's avatar
Raymond Toy committed
26
(in-package #:oct)
27

Raymond Toy's avatar
Raymond Toy committed
28
(named-readtables:in-readtable oct-readtable))
29

30
31
32
33
34
35
36
37
38
(defun qd-scale-exponent (original-x)
  (let* ((x original-x))
    (multiple-value-bind (sig exponent)
	(decode-float x)
      (declare (ignore sig))
      (if (= x #q0)
	  (values #q0 1)
	  (let* ((ex (round (* exponent (log #q2 10))))
		 (x (if (minusp ex)
39
40
			
			(* x #q10.0q0 (expt #q10.0q0 (- (- ex) 1)))
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
			(/ x #q10.0q0 (expt #q10.0q0 (1- ex))))))
	    (do ((d #q10.0q0 (* d #q10.0q0))
		 (y x (/ x d))
		 (ex ex (1+ ex)))
		((< y #q1.0q0)
		 (do ((m #q10.0q0 (* m #q10.0q0))
		      (z y (* y m))
		      (ex ex (1- ex)))
		     ((>= z #q0.1q0)
		      (values z ex))))))))))

(defun decimal-string (n)
  (cl:write-to-string n :base 10 :radix nil :escape nil))

(defun qd-format-exp-aux (stream number w d e k ovf pad marker atsign)
  (multiple-value-bind (num expt)
      (qd-scale-exponent (abs number))
    (let* ((expt (- expt k))
	   (estr (decimal-string (abs expt)))
	   (elen (if e (max (length estr) e) (length estr)))
	   (add-zero-p nil))
      (if (and w ovf e (> elen e))	;exponent overflow
	  (dotimes (i w)
	    (write-char ovf stream))
	  (let* ((fdig (if d (if (plusp k) (1+ (- d k)) d) nil))
		 (fmin (if (minusp k)
			   1
			   fdig))
		 (spaceleft (if w
				(- w 2 elen
				   (if (or atsign (minusp (float-sign number)))
				       1 0))
				nil)))
	    (multiple-value-bind (fstr flen lpoint tpoint)
75
		(octi::qd-to-string (qd-value num) spaceleft fdig k fmin)
76
77
	      (when (and d (zerop d)) (setq tpoint nil))
	      (when w 
78
		(cl:decf spaceleft flen)
79
80
81
82
83
84
85
86
		;; See CLHS 22.3.3.2.  "If the parameter d is
		;; omitted, ... [and] if the fraction to be
		;; printed is zero then a single zero digit should
		;; appear after the decimal point."  So we need to
		;; subtract one from here because we're going to
		;; add an extra 0 digit later.
		(when (and (null d) (char= (aref fstr (1- flen)) #\.))
		  (setf add-zero-p t)
87
		  (cl:decf spaceleft))
88
89
		(when lpoint
		  (if (or (> spaceleft 0) tpoint)
90
		      (cl:decf spaceleft)
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
		      (setq lpoint nil)))
		(when (and tpoint (<= spaceleft 0))
		  (setq tpoint nil)))
	      (cond ((and w (< spaceleft 0) ovf)
		     ;;significand overflow
		     (dotimes (i w) (write-char ovf stream)))
		    (t (when w
			 (dotimes (i spaceleft)
			   (write-char pad stream)))
		       (if (minusp (float-sign number))
			   (write-char #\- stream)
			   (if atsign (write-char #\+ stream)))
		       (when lpoint (write-char #\0 stream))
		       (write-string fstr stream)
		       ;; Add a zero if we need it.  Which means
		       ;; we figured out we need one above, or
		       ;; another condition.  Basically, append a
		       ;; zero if there are no width constraints
		       ;; and if the last char to print was a
		       ;; decimal (so the trailing fraction is
		       ;; zero.)
		       (when (or add-zero-p
				 (and (null w)
				      (char= (aref fstr (1- flen)) #\.)))
			 ;; It's later and we're adding the zero
			 ;; digit.
			 (write-char #\0 stream))
		       (write-char (if marker
				       marker
				       #\q)
				   stream)
		       (write-char (if (minusp expt) #\- #\+) stream)
		       (when e 
			 ;;zero-fill before exponent if necessary
			 (dotimes (i (- e (length estr)))
			   (write-char #\0 stream)))
		       (write-string estr stream)))))))))

(defun qd-format-exp (stream arg colon-p at-sign-p
toy's avatar
toy committed
130
		      &optional w d e (k 1) ovf (pad #\space) exp-marker)
131
132
  (declare (ignore colon-p))
  (qd-format-exp-aux stream arg w d e k ovf pad exp-marker at-sign-p))