This is a read-only archive of lispforum.com. The forum was locked to new users and posts and is preserved here as static HTML from a database snapshot taken on 2019-09-07.

Using binary arithmetics in collect statement?

6 posts · 2045 views

Hello. I'm trying to figure out how would I write the same thing, but using "collect", because it seems like a better way to do the same thing. Here's my code:
;; 0.123456
(defparameter *test-float* (list #x3F #xBF #x9A #xCF #xFA #x7E #xB6 #xBF))

(defun decode-ieee-754 (bytes)
	(let (	(sign (if (logbitp 1 (first bytes)) 1 -1))
		(exponent (- (logior (ash (logand (first bytes) #x7F) 4) 
			(ash (second bytes) -4)) 1023))
		(significand (+ 4503599627370496 
			(ash (logand (second bytes) #xF) 48) 
			(loop for i from 2 
				with result = 0
				do (setf result (+ (ash result 8) (nth i bytes)))
				; This looks ugly, I'd rather use "collect" here
				when (= i 7) return result))))
	(format t "~&sign:~15T~b2,~&exponent:~15T~b2,~&significand:~15T~b2~&" sign exponent significand)
	(* sign (/ significand (ash 1 (- 52 exponent))))))

(format t "result: ~F~&" (decode-ieee-754 *test-float*))
(Yes, I know there is already a library doing this, and, for this function to be correct I'd need to handle both infinities and nans, but, I'm doing this as a learning exercise, so, I'm more bothered with better ways of utilizing basic features like loops, type system and so on).
Or if you think, that "collect" is not the way to go, please tell, what, in your opinion would be the proper way to do it. If you could explain it, it would be golden!
TIA :)

EDIT: I've rewritten it like so:
;; 0.123456
(defparameter *test-float* (list #x3F #xBF #x9A #xCF #xFA #x7E #xB6 #xBF))

(defun decode-ieee-754 (bytes)
	(let (	(sign (if (logbitp 1 (first bytes)) 1 -1))
		(exponent (- (logior (ash (logand (first bytes) #x7F) 4) 
			(ash (second bytes) -4)) 1023))
		(significand (+ 4503599627370496 
			(ash (logand (second bytes) #xF) 48)
			(reduce #'(lambda (x y) (+ (ash x 8) y)) 
				(subseq bytes 2)))))
	(format t "~&sign:~15T~b2,~&exponent:~15T~b2,~&significand:~15T~b2~&" sign exponent significand)
	(* sign (/ significand (ash 1 (- 52 exponent))))))

(format t "result: ~F~&" (decode-ieee-754 *test-float*))
Which looks "cleaner" to me, yet I'd like to know about my original question about "collect".

Re: Using binary arithmetics in collect statement?

wvxvw wrote:I'm trying to figure out how would I write the same thing, but using "collect"
Collect does not make sense in this context, because "collect" is for constructing lists. You can use the "for =" construct of loops for this.
(defun decode-ieee-754 (bytes)
  (let ((sign (if (logbitp 1 (first bytes)) 1 -1))
        (exponent (- (logior (ash (logand (first bytes) #x7F) 4)
                             (ash (second bytes) -4)) 1023))
        (significand (+ 4503599627370496
                        (ash (logand (second bytes) #xF) 48)
                        (loop for byte in (cddr bytes)
                              for result =  (+ (ash (or result 0) 8) byte)
                              finally (return result)))))
    (format t "~&sign:~15T~b2,~&exponent:~15T~b2,~&significand:~15T~b2~&" sign exponent significand)
    (* sign (/ significand (ash 1 (- 52 exponent))))))

Re: Using binary arithmetics in collect statement?

Thank you!
Now, if I may, a few questions. In general, if I can do (nth 0 list) and (first list), for example, which should I use? Likewise, subseq vs cddr in this case - is this a matter of personal preference, or is one variant in some way better then the other?
Won't I get (and if not, why) a sort of forward-reference error, if I do this:
. . .
for result =  (+ (ash (or result 0) 8) byte)
. . .
I.e. technically, I'd be referencing a variable before it was declared.

Re: Using binary arithmetics in collect statement?

wvxvw wrote:Now, if I may, a few questions. In general, if I can do (nth 0 list) and (first list), for example, which should I use?
When the index is constant the specific function form is probably better style. Especially since NTH shouldn't be really used, since it might become tempting to use it to access elements further into a list, and since list access is linear in index, it is easy to accidentally turn algorithms quadratic.

The CDDR versus SUBSEQ is mostly personal preference. There is a school of thought that says that the most specific construct should be usually used (so for example for comparing numbers use '=' rather than EQL, even in cases where they would be equivalent). There is no noticeable performance effect.
wvxvw wrote:Won't I get (and if not, why) a sort of forward-reference error, if I do this:
Looking at the standard this code was not conforming, although it does work on SBCL, because for that LOOP implementation such variables are bound to NIL outside the LOOP. What I should have written was:
for result =  byte then (+ (ash result 8) byte)

Re: Using binary arithmetics in collect statement?

Aha, thank you! That makes sense now.

Re: Using binary arithmetics in collect statement?

A correction:

(SUBSEQ seq 2) and (CDDR seq) differ because (SUBSEQ ....) always returns a copy of the relevant list structure, and (CDDR ...) always returns shared structure.