Tag Archives: obscure commands

The less-familiar parts of Lisp for beginners — scale-float and related functions

Now we come to a set of Lisp functions that are obscure because the operations they perform are fairly esoteric.  These functions are decode-float, scale-float, float-radix, float-sign, float-digits, float-precision, and integer-decode-float.  Many of these functions have direct analogues in glibc.  Note that the glibc functions return values as if the floating-point radix is 2, while the Lisp functions use the true radix, whatever that might be, whose value can be retrieved with float-radix.

  • decode-float is almost equivalent to frexp(), frexpf(), and frexpl(), but its first returned value is always positive, and the sign is present in the third returned value, while the glibc function produces a negative value for the normalized fraction if the input is negative.
  • scale-float is equivalent to ldexp(), ldexpf(), ldexpl(), or scalb(), scalbf(), and scalbl() in BSD.  You would typically use it to reassemble a number you had decoded with decode-float or integer-decode-float after the parameters had been adjusted for your purposes.
  • float-radix returns the radix for a particular float value.  The POSIX equivalent is the FLT_RADIX macro.
  • float-sign behaves like copysign(), but with the arguments reversed.
  • float-digits returns the number of base-radix digits in the representation of the floating-point number.  It autodetects the type of its argument, and returns appropriate value.  The POSIX equivalents are FLT_MANT_DIG for floats, and DBL_MANT_DIG for doubles, and LDLB_MANT_DIG for long doubles.
  • float-precision returns the number of significant digits in the argument.  For normalized floating-point values, this is the same as float-digits, otherwise it may be lower.  There is no direct analogue in glibc, but by using fpclassify() and frexpf(), the programmer can detect subnormal floating-point values and take appropriate action.
  • integer-decode-float is much like decode-float, but scales up the normalized fraction to an integer, adjusting the exponent appropriately.  There is no analogue in glibc.

If you have not run across these glibc functions and macros, you’re not likely to be interested in their Lisp equivalents.  These functions are used for specialized manipulations of floating-point numbers.  Some of them are useful to detect the floating-point parameters on a particular platform, possibly altering algorithms or data types to improve behaviour on unusual hardware.  Others are more useful during the execution of algorithms.

I have been doing numerical simulations and floating-point work for a long time, but of all these functions, I’ve only had to use frexpf() and ldexpf() in production code.  In setting up a mathematical model on a mesh, with user-supplied node coordinates as single-precision floats, some of the calculations to be done would fail badly if two nodes were too close together.  The code could handle the nodes being coincident, or reasonably far apart, but when the floating-point representation of a coordinate value of two nodes differed by only a few floating-point quanta, the subsequent calculations would fail.  To fix this problem, I wanted to round off the node coordinates on input to some very fine square grid, maybe one spaced 8 floating-point quanta apart, and be able to do this whether the input mesh coordinates were spanning thousands of metres, of thousandths of metres.  The solution I used, in C++, looks like this:
binrnd.c

float binaryround(float x)
{
    int exponent;
    float prefactor = frexpf(x, &exponent);
    prefactor = (int) (prefactor * 1048576 + 0.5);
    exponent -= 20;
    return ldexpf(prefactor, exponent);
}

The equivalent in Lisp would be this:
binrnd.lisp
(defun binaryround (x)
  (multiple-value-bind (normalized exp sgn)
      (integer-decode-float x)
    (setf normalized (/ (+ 4 normalized) 8))
    (* (scale-float (float normalized)
                    (+ 3 exp)) 
       sgn)))

Finally, I’d like to point something out.  If you’re starting to do serious floating-point work on computers, you probably need to research some of the subtle tricks and dangers involved.  A good source is this: What Every Computer Scientist Should Know About Floating-Point Arithmetic.

The less-familiar parts of Lisp for beginners — row-major-aref

We now come to a fairly useful Lisp feature, but again, one that might not have been covered in a basic introduction to Lisp.  The row-major-aref accessor allows the programmer to reference an element in a multi-dimensional array using only a single index.  For operations that require traversing the entire array, this lets the programmer visit all cells without needing to write nested loops, and even allows the code to operate on arrays of different dimensions.  Here is an example of this in use:
*slime-repl sbcl*

CL-USER> (defun sum-array-elements (array)
           (let ((sum 0.0d0))
             (dotimes (i (array-total-size array))
               (incf sum (row-major-aref array i)))
             sum))
SUM-ARRAY-ELEMENTS
CL-USER> (let ((2d-array (make-array (list 10 10)
                                     :initial-element 2))
               (3d-array (make-array (list 10 10 10)
                                     :initial-element 3)))

           (format t "Sum of elements in 2d-array: ~G~%"
                   (sum-array-elements 2d-array))
           (format t "Sum of elements in 3d-array: ~G~%"
                   (sum-array-elements 3d-array)))
Sum of elements in 2d-array: 200.    
Sum of elements in 3d-array: 3000.    
NIL

The less-familiar parts of Lisp for beginners — rotatef

Another somewhat obscure feature of Lisp is rotatef.  The rotatef function takes as its arguments a set of places (variables or forms that can be assigned to with setf, think l-value from C++).  It then rotates them, so that the value of the second is moved to the first place, the value of the third moved to the second, and so on, with the value of the first place being moved to the last place.

Calling
*slime-repl sbcl*

CL-USER> (rotatef place1 place2 place3)

is almost like calling
*slime-repl sbcl*
CL-USER> (psetf place1 place2
                place2 place3
                place3 place1)

with the caveat that the psetf example will evaluate each place twice, so if there are side-effects, the behaviour may not be as expected.

Finally, here is an example of using rotatef on places:
*slime-repl sbcl*

CL-USER> (let ((list1 (list 1 2 3 4))
               (list2 (list :A :B :C :D))
               (list3 (list "a" "b" "c" "d")))
           (rotatef (cdr list1) (cdr list2) (cdr list3))
           (values list1 list2 list3))
(1 :B :C :D)
(:A "b" "c" "d")
("a" 2 3 4)

The less-familiar parts of Lisp for beginners — restart-case

Having just talked about restart-bind, we’ll now look at restart-case.  The restart-bind might be used to implement restart-case, but there are differences between the syntax and behaviour for these two macros.  The most obvious difference from the programmer’s perspective is in what happens after a restart is invoked.  In the case of restart-bind, the execution continues after the invoke-restart call.  In the case of restart-case, the entire restart-case form exits, and the values returned by the restart, if any, are returned.  Here is a simple example of the two different behaviours:
*slime-repl sbcl*

CL-USER> (restart-bind
             ((say-hi #'(lambda ()
                          (format t "Hello~%")
                          (values 1 2 3))))
           (progn
             (format t "Printing before the restart~%")
             (invoke-restart 'say-hi)
             (format t "Printing after the restart~%")
             (format t "Returning the value 10~%")
             10))
Printing before the restart
Hello
Printing after the restart
Returning the value 10
10
CL-USER> (restart-case
             (progn
               (format t "Printing before the restart~%")
               (invoke-restart 'say-hi)
               (format t "Printing after the restart~%")
               (format t "Returning the value 10~%")
               10)
           (say-hi ()
             (format t "Hello~%")
             (values 1 2 3)))

Printing before the restart
Hello
1
2
3

The less-familiar parts of Lisp for beginners — restart-bind

Continuing through the list of somewhat obscure Lisp features, we come to restart-bind.  This is a fairly low-level tool for configuring restarts.  A review of Lisp restarts is available in this earlier article.  While the restart system can appear complicated, it is generally useful to think of restarts as dynamically-scoped functions that can be invoked under certain conditions.  The restart-bind macro is the low-level glue to handle this, and it’s unusual that the full power of this macro is needed.  If you can achieve your intended results with restart-case, it is recommended that you use that, instead.

The restart-bind macro has three optional key/value pair arguments that affect behaviour in interactive contexts.

A simple example of the basic syntax follows.  Note that this example could also be easily coded with restart-case:
*slime-repl sbcl*

CL-USER> (restart-bind
             ((say-hi #'(lambda ()
                          (format t "Hello~%"))))
           (dotimes (i 10)
             (format t "i= ~D~%" i)
             (when (= (mod i 4) 3)
               (invoke-restart 'say-hi))))
i= 0
i= 1
i= 2
i= 3
Hello
i= 4
i= 5
i= 6
i= 7
Hello
i= 8
i= 9
NIL