[racket] Fun with denormalized floating point numbers

From: Michael Wilber (mwilber at uccs.edu)
Date: Wed Aug 1 14:40:04 EDT 2012

(oops, forgot to cc: the list)

Oh wow, that's interesting. Your code on my computer:

subnormal addition:
cpu time: 8156 real time: 8171 gc time: 0
cpu time: 9009 real time: 9035 gc time: 0
cpu time: 7740 real time: 7765 gc time: 0
cpu time: 7276 real time: 7288 gc time: 0
cpu time: 10643 real time: 10743 gc time: 0

normal addition:
cpu time: 286 real time: 289 gc time: 0
cpu time: 337 real time: 334 gc time: 0
cpu time: 353 real time: 357 gc time: 0
cpu time: 530 real time: 536 gc time: 0
cpu time: 600 real time: 599 gc time: 0

My computer's pretty old though. According to /proc/cpuinfo, it's a Core
Duo (Yonah, http://en.wikipedia.org/wiki/Yonah_(microprocessor) )
1.8Ghz, c.a. 2006, with a 32-bit OS.

Trying again with an Intel Xeon 3Ghz 64-bit machine yields similar results:

subnormal addition:
cpu time: 7260 real time: 7248 gc time: 0
cpu time: 7312 real time: 7300 gc time: 0
cpu time: 7245 real time: 7233 gc time: 0
cpu time: 7256 real time: 7247 gc time: 0
cpu time: 7285 real time: 7273 gc time: 0

normal addition:
cpu time: 248 real time: 251 gc time: 0
cpu time: 292 real time: 295 gc time: 0
cpu time: 240 real time: 240 gc time: 0
cpu time: 248 real time: 251 gc time: 0
cpu time: 296 real time: 296 gc time: 0

Fascinating. I wonder if your machine has better floating point hardware
So maybe this is just something to watch out for on older chips.

Thanks for looking into this!

On Wed, 1 Aug 2012 11:01:13 -0700, Neil Toronto <neil.toronto at gmail.com> wrote:
> 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 ⊥
>
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users


Posted on the users mailing list.