Coercing to double-float

Discussion of Common Lisp
Post Reply
Harnon
Posts: 78
Joined: Wed Jul 30, 2008 9:59 am

Coercing to double-float

Post by Harnon » Tue Jun 16, 2009 1:37 pm

Quite simply, i need to use the type double for a foreign function argument. When I do the following:
(coerce .2 'double-float)
it returns on Allegro 8.1 running under windows vista 32 bit
0.22200000286102295d0
Why is the new number different??? What's going on :roll:

simon
Posts: 16
Joined: Wed May 13, 2009 9:12 am

Re: Coercing to double-float

Post by simon » Tue Jun 16, 2009 2:13 pm

Don't forget that floating point numbers are just rational approximations, you can't get around issues of precision.

Code: Select all

CL-USER> (integer-decode-float .2d0)
7205759403792794
-55
1
CL-USER> (integer-decode-float .2)
13421773
-26
1
Your implementation may not have the exact same representations as mine, but it doesn't matter. Fundamentally he issue is this:

Code: Select all

CL-USER> (/ 7205759403792794 13421773)
7205759403792794/13421773
i.e., the double-float representation of 2/10 is not divisible by the single-float representation of 2/10. But look at your coerced version:

Code: Select all

CL-USER> (integer-decode-float (coerce .2 'double-float))
7205759511166976
-55
1
CL-USER> (/ 7205759511166976 13421773)
536870912
In order to coerce your .2 to double float, youscale the significand to the new representation, and adjust the exponent as needed. You don't have the real number ".2" around to return to. This has nothing to do with lisp per se, it's in the nature of floating point representations.

Hope that makes sense.

nuntius
Posts: 538
Joined: Sat Aug 09, 2008 10:44 am
Location: Newton, MA

Re: Coercing to double-float

Post by nuntius » Tue Jun 16, 2009 4:13 pm

0.2 and 0.222 are distinctly different numbers in any common floating-point representation. The behavior you show is quite unusual; it is not normal rounding due to type conversion (and double-floats can store all single-floats without loss). On my machine, SBCL, CCL, CLISP, and ECL all give 0.20000000298023224d0.

Not exactly sure what you're seeing, but agree that its worth investigating. What does (* 0.2 1d0) give?

Harnon
Posts: 78
Joined: Wed Jul 30, 2008 9:59 am

Re: Coercing to double-float

Post by Harnon » Tue Jun 16, 2009 7:27 pm

Sorry. (coerce .2 'double-float) produces
0.20000000298023224d0

(* .2 1d0) produces 0.20000000298023224d0

(* .2d0 .1d0) produces
0.020000000000000004d0

I think I will just stick to the float version of the c library. Thanks for the explanation!

simon
Posts: 16
Joined: Wed May 13, 2009 9:12 am

Re: Coercing to double-float

Post by simon » Tue Jun 16, 2009 10:58 pm

Harnon wrote:Sorry. (coerce .2 'double-float) produces
0.20000000298023224d0

(* .2 1d0) produces 0.20000000298023224d0

(* .2d0 .1d0) produces
0.020000000000000004d0

I think I will just stick to the float version of the c library. Thanks for the explanation!
Huh, strange that I completely missed your typo earlier, reading it as the above behavior (as you've corrected it)

This behavior is the expected one, as noted.
(and double-floats can store all single-floats without loss)
Is not particularly useful in this actual situation. The issue isn't whether or not you can represent 2/10 in both representations, but how you choose to map the smaller representation to the larger one, which obviously cannot be done bijectively. In this context there is nothing special about "0.2d0" compared to "0.20000000298023224d0", they both map to 0.2 in single precision.

All you can really hope for is consistency, which you have:

Code: Select all

CL-USER> (float .2 1d0)
0.20000000298023224d0
CL-USER> (float (float .2 1d0) 1.0)
0.2
CL-USER> (float .2d0 1.0)
0.2
This is worth a read on the subject if you want to see some of the details: What Every Computer Scientist Should Know About Floating-Point Arithmetic

Post Reply