1 ;; encode-decode-float.lisp
2 ;; Copyright 2007 by Robert Dodier
4 ;; This program is free software; you can redistribute it and/or
5 ;; modify it under the terms of the GNU General Public License.
7 ;; This program has NO WARRANTY, not even the implied warranty of
8 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 ;; These functions encode integers into 64 bit IEEE 754 floats
11 ;; and decode 64 bit floats into 64 bit integers.
12 ;; These functions cannot handle any other size of float.
14 ;; Encode float-64 to integer: SMASH-FLOAT-64-INTO-INTEGER
15 ;; Decode integer to float-64: CONSTRUCT-FLOAT-64-FROM-INTEGER
17 ;; Write float-64 to output stream: WRITE-FLOAT-64
18 ;; Read float-64 from input stream: READ-FLOAT-64
19 ;; Read an unsigned integer (of any size) from input stream: READ-UNSIGNED-INTEGER
20 ;; Write an unsigned integer (of any size) to output stream: WRITE-UNSIGNED-INTEGER
21 ;; Set assumed external byte order for input and output: DEFINE-EXTERNAL-BYTE-ORDER
25 (defun smash-float-into-integer (x)
27 (significand exponent sign
)
28 (integer-decode-float x
)
29 ;; This logic cannot be guaranteed to work -- there is no necessary
30 ;; correlation between IEEE 754 and CL floats. Oh well.
31 (if (or (typep x
'double-float
) (typep x
'long-float
))
32 (smash-decoded-float-64-into-integer significand exponent sign
)
33 (smash-decoded-float-32-into-integer significand exponent sign
))))
35 (defun smash-decoded-float-32-into-integer (significand exponent sign
)
48 (defun smash-decoded-float-64-into-integer (significand exponent sign
)
61 (defun construct-float-64-from-integer (x)
63 (significand exponent sign
)
64 (extract-smashed-float-64-from-integer x
)
65 (* sign
(scale-float (float significand
1d0
) exponent
))))
67 (defun extract-smashed-float-64-from-integer (x)
71 ((significand (dpb x
(byte 52 0) #x10000000000000
))
72 (exponent (- (ldb (byte 11 52) x
) 1023 52))
73 (sign (if (eql (ldb (byte 1 63) x
) 0) 1 -
1)))
74 (values significand exponent sign
))))
76 ;; Stream input and output
78 (defun write-float (x s
)
79 (write-unsigned-integer (smash-float-into-integer x
) (size-in-bytes x
) s
))
81 (defun size-in-bytes (x)
82 (if (or (typep x
'double-float
) (typep x
'long-float
)) 8 4)) ;; AUGHHHH!! THIS IS TERRIBLE!
84 (defun read-float-64 (s)
85 (let ((x (read-unsigned-integer 8 s
)))
86 (if (eq x
'eof
) 'eof
(construct-float-64-from-integer x
))))
88 ;; READ-UNSIGNED-INTEGER, WRITE-UNSIGNED-INTEGER, and associated
89 ;; byte order stuff adapted from read-bytes-standalone.lisp,
90 ;; by Martin Raspaud and Robert Strandh,
91 ;; which was released under terms of GNU GPL v2 or later.
93 (deftype external-byte-order
()
94 "Defines the legal values for *EXTERNAL-BYTE-ORDER*."
97 (defvar *external-byte-order
* :msb
98 "*EXTERNAL-BYTE-ORDER* must be either :msb or :lsb")
100 (defun define-external-byte-order (x)
101 (check-type x external-byte-order
)
102 (setf *external-byte-order
* x
))
104 (defun read-unsigned-integer (nb-bytes s
)
105 "Read an unsigned integer of size NB-BYTES bytes from input stream S."
106 (if (zerop nb-bytes
) 0
108 (dotimes (i nb-bytes
)
109 (let ((x (read-byte s nil
'eof
)))
111 (return-from read-unsigned-integer
'eof
)
112 (setq bytes
(nconc bytes
(list x
))))))
113 (case *external-byte-order
*
115 (mapc #'(lambda (x) (setq y
(+ x
(ash y
8)))) (nreverse bytes
)))
117 (mapc #'(lambda (x) (setq y
(+ x
(ash y
8)))) bytes
)))
120 (defun write-unsigned-integer (quantity nb-bytes s
)
121 "Write an unsigned integer of size NB-BYTES bytes to output stream S."
122 (case *external-byte-order
*
124 (unless (zerop nb-bytes
)
125 (write-byte (logand quantity
#xff
) s
)
126 (write-unsigned-integer
131 (unless (zerop nb-bytes
)
132 (write-unsigned-integer
136 (write-byte (logand quantity
#xff
) s
)))))