[racket] Fun with denormalized floating point numbers

From: Neil Toronto (neil.toronto at gmail.com)
Date: Wed Aug 1 14:01:13 EDT 2012

I wonder how out-of-date the advice is to avoid subnormal numbers. I 
don't see any slowdown in my tests. I've written what I think is a good 
test program, and I have some experience with squeezing floating-point 
performance out of Racket. Can we get others on the mailing list to run 
it and report some timings?

The following test program should ensure (currently) that the JIT 
compiles the operations to their fastest forms, that floats don't get 
boxed, and that the compiler doesn't inline 1.0.


#lang racket

(require unstable/flonum
          racket/flonum
          racket/unsafe/ops)

;; Don't allow Racket to inline `one':
(define (make-one) 1.0)
(define one (make-one))

;; Store floats here to make sure they don't get boxed:
(define res (make-flvector 1))

(printf "subnormal addition:~n")
(for ([_  (in-range 5)])
   (time (for ([_  (in-range 20000000)])
           (unsafe-flvector-set! res 0 (unsafe-fl+ +min.0 +min.0)))))

(printf "~nnormal addition:~n")
(for ([_  (in-range 5)])
   (time (for ([_  (in-range 20000000)])
           (unsafe-flvector-set! res 0 (unsafe-fl+ one one)))))


Running this in DrRacket, with debug info turned off, on a one-year-old 
laptop, I get:

subnormal addition:
cpu time: 80 real time: 88 gc time: 0
cpu time: 100 real time: 93 gc time: 0
cpu time: 90 real time: 92 gc time: 0
cpu time: 110 real time: 106 gc time: 0
cpu time: 90 real time: 94 gc time: 0

normal addition:
cpu time: 90 real time: 87 gc time: 0
cpu time: 90 real time: 93 gc time: 0
cpu time: 90 real time: 85 gc time: 0
cpu time: 100 real time: 100 gc time: 0
cpu time: 90 real time: 93 gc time: 0

Which is obviously the same.

FWIW, subnormal numbers ensure that subtraction doesn't underflow, no 
matter how close two flonums are. The smallest positive flonum is +min.0 
(currently defined in `unstable/flonum'):

 > +min.0
4.9406564584125e-324

Subtracting the next one from the one after returns something nonzero:

 > (flstep +min.0 1)
9.8813129168249e-324
 > (flstep +min.0 2)
1.4821969375237e-323
 > (- (flstep +min.0 2) (flstep +min.0 1))
4.9406564584125e-324

Neil ⊥


Posted on the users mailing list.