Using binary arithmetics in collect statement?

Discussion of Common Lisp
Post Reply
wvxvw
Posts: 127
Joined: Sat Mar 26, 2011 6:23 am

Using binary arithmetics in collect statement?

Post by wvxvw » Fri Apr 01, 2011 4:11 am

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:

Code: Select all

;; 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:

Code: Select all

;; 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".

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: Using binary arithmetics in collect statement?

Post by ramarren » Fri Apr 01, 2011 10:36 am

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.

Code: Select all

(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))))))

wvxvw
Posts: 127
Joined: Sat Mar 26, 2011 6:23 am

Re: Using binary arithmetics in collect statement?

Post by wvxvw » Fri Apr 01, 2011 12:06 pm

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:

Code: Select all

. . .
for result =  (+ (ash (or result 0) 8) byte)
. . .
I.e. technically, I'd be referencing a variable before it was declared.

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: Using binary arithmetics in collect statement?

Post by ramarren » Fri Apr 01, 2011 1:11 pm

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:

Code: Select all

for result =  byte then (+ (ash result 8) byte)

wvxvw
Posts: 127
Joined: Sat Mar 26, 2011 6:23 am

Re: Using binary arithmetics in collect statement?

Post by wvxvw » Sat Apr 02, 2011 12:13 am

Aha, thank you! That makes sense now.

pjstirling
Posts: 166
Joined: Sun Nov 28, 2010 4:21 pm

Re: Using binary arithmetics in collect statement?

Post by pjstirling » Sun Apr 03, 2011 5:00 pm

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.

Post Reply