Seibel's book, PCL, has a section on parsing binary files. He gives an
example of reading an unsigned 16-bit thing:
(defun read-ui16 (stream)
(let ((ui16 0))
(setf (ldb (byte 8 8) ui16) (read-byte stream))
(setf (ldb (byte 8 0) ui16) (read-byte stream))
ui16))
So far, so good. How would a Lisp expert read a signed 16-bit thing?
One might, for example, write (sign-ui16 (read-ui16 stream)), where
sign-ui16 is defined as follows:
(defun sign-ui16 (n)
(if (zerop (ldb (byte 1 15) n))
n
(- (1+ (logxor #xffff n)))))
If the sign-bit is zero, return the number. If it's set, return -(~n +
1). (Note: lognot doesn't work - it has to be logxor).
Any suggestions on a better way to do this?
····@stablecross.com (Bob Felts) writes:
> So far, so good. How would a Lisp expert read a signed 16-bit thing?
>
> One might, for example, write (sign-ui16 (read-ui16 stream)), where
> sign-ui16 is defined as follows:
>
> (defun sign-ui16 (n)
> (if (zerop (ldb (byte 1 15) n))
> n
> (- (1+ (logxor #xffff n)))))
>
> If the sign-bit is zero, return the number. If it's set, return -(~n +
> 1). (Note: lognot doesn't work - it has to be logxor).
>
> Any suggestions on a better way to do this?
Christophe Rhodes showed me this general idea:
(defun sign-sized (n size)
(if (logbitp (1- size) n)
(dpb n (byte size 0) -1)
n))
So:
(defun sign-ui16 (n)
(sign-sized n 16))
Zach
* (Bob Felts) <···························@stablecross.com> :
Wrote on Sun, 14 Dec 2008 00:38:13 -0500:
| (defun sign-ui16 (n)
| (if (zerop (ldb (byte 1 15) n))
| n
| (- (1+ (logxor #xffff n)))))
|
| If the sign-bit is zero, return the number. If it's set, return -(~n +
| 1). (Note: lognot doesn't work - it has to be logxor).
|
| Any suggestions on a better way to do this?
Here is general code from Pascal Bourguignon
(defun unsigned-to-signed/2-complement (x width)
(declare (integer x width))
(let ((maxpos+1 (expt 2 (1- width))))
(if (< x maxpos+1) x (- x (* 2 maxpos+1)))))
Most of the code I've seen which reads signed 16bit words is along these
lines: declare the variable to be an unsigned-byte 16, check and
subtract 65536, or just return n.
This does not address how n was read from the external file, endianness
in which the file was stored. For parsing binary files I've been using
frodef's excellent
<URL:http://www.cs.uit.no/~frodef/sw/binary-types/>
(from even before PCL came out.) There you'd do
(read-binary 's16 stream)
--
Madhu
Madhu <·······@meer.net> wrote:
> * (Bob Felts) <···························@stablecross.com> :
> Wrote on Sun, 14 Dec 2008 00:38:13 -0500:
>
> | (defun sign-ui16 (n)
> | (if (zerop (ldb (byte 1 15) n))
> | n
> | (- (1+ (logxor #xffff n)))))
> |
> | If the sign-bit is zero, return the number. If it's set, return -(~n +
> | 1). (Note: lognot doesn't work - it has to be logxor).
> |
> | Any suggestions on a better way to do this?
>
> Here is general code from Pascal Bourguignon
>
> (defun unsigned-to-signed/2-complement (x width)
> (declare (integer x width))
> (let ((maxpos+1 (expt 2 (1- width))))
> (if (< x maxpos+1) x (- x (* 2 maxpos+1)))))
>
> Most of the code I've seen which reads signed 16bit words is along these
> lines: declare the variable to be an unsigned-byte 16, check and
> subtract 65536, or just return n.
>
> This does not address how n was read from the external file, endianness
> in which the file was stored. For parsing binary files I've been using
> frodef's excellent
>
> <URL:http://www.cs.uit.no/~frodef/sw/binary-types/>
>
> (from even before PCL came out.) There you'd do
>
> (read-binary 's16 stream)
>
Thanks. Excellent information.