[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Double floats



>I am tring to take a double-float number, break it down into 2 long
>integers (high-word and low-word) to send over the network conforming to
>the IEEE floating point standard.  I also want to read 2 words from the
>stream and make a double-float from them.  Any Ideas?  Symbolics has two
>functions that do what I want: "si:dfloat-components: (double)" &
>"si:%make-double: (high low)"  Any way to do this with the mac?
>
>Thanks,
>millett@sbctri.sbc.com

MCL has a function that breaks a double float into 4 fixnums:
high-mantissa, low-mantissa, exponent, and sign.
It also has a function for putting these components back together again.

ccl::fixnum-decode-float double-float
; decompose a double-float into fixnum size pieces
; returns 4 values
; hi is high 24 bits of mantissa (with the implied 1 in bit 25 if appropriate)
; lo is low 28 bits of mantissa  (hi and lo are both right justified)
; exp is 11 bit exponent (the bits as they are - not unbiased; i.e. exp is >= 0)
; sign is 1 or -1 

ccl::make-float-from-fixnums hi lo exp sign
; make a float from hi - high 24 bits mantissa (ignore implied higher bit)
;                   lo -  low 28 bits mantissa
;                   exp  - take low 11 bits
;                   sign - sign(sign) => result
; hi result - 1 bit sign: 11 bits exp: 20 hi bits of hi arg
; lo result - 4 lo bits of hi arg: 28 lo bits of lo arg
; no error checks, no tweaks, no nuthin

You can use these to make the integers (likely bignums) that you want:

(defun dfloat-components (dfloat)
  (multiple-value-bind (hi lo exp sign) (ccl::fixnum-decode-float dfloat)
    (declare (fixnum hi lo exp sign))
    (values
     (+ (if (< sign 0) (ash 1 31) 0)
        (ash exp (- 31 11))
        (ash (logand hi #xfffff0) (- 20 24)))
     (+ (ash (logand hi #xf) (- 32 4))
        lo))))

(defun make-double (high low)
  (ccl::make-float-from-fixnums
   (+ (ash (logand high #xfffff) 4)
      (ash low -28))
   (logand low #xfffffff)
   (logand (ash high -20) #x7ff)
   (if (logbitp 31 high) -1 1)))

This is not very efficient.
The following LAP versions are more efficient
(though dfloat-components still conses bignums):

(in-package :ccl)

(eval-when (:compile-toplevel :execute)
  (require "LAPMACROS")                 ; lap-inline
  (require "LISPEQU"))                  ; $floathi

(defun dfloat-components (dfloat)
  (setq dfloat (require-type dfloat 'double-float))
  (let (hi lo)
    (lap-inline ()
      (:variable dfloat hi lo)
      (move.l (varg dfloat) atemp0)
      (move.l (atemp0 $floathi) arg_z)
      (jsr_subprim $sp-mklong)
      (move.l acc (varg hi))
      (move.l (varg dfloat) atemp0)
      (move.l (atemp0 (+ $floathi 4)) arg_z)
      (jsr_subprim $sp-mklong)
      (move.l acc (varg lo)))
    (values hi lo)))
      
(defun make-float (high low)
  (lap-inline ()
    (:variable high low)
    (move.l (varg high) arg_z)
    (jsr_subprim $sp-getxlong)
    (move.l acc arg_y)
    (move.l (varg low) arg_z)
    (jsr_subprim $sp-getxlong)
    (jsr_subprim $sp-makefloat)))