Page 1 of 1

Coercing to double-float

Posted: Tue Jun 16, 2009 1:37 pm
by Harnon
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:

Re: Coercing to double-float

Posted: Tue Jun 16, 2009 2:13 pm
by simon
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.

Re: Coercing to double-float

Posted: Tue Jun 16, 2009 4:13 pm
by nuntius
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?

Re: Coercing to double-float

Posted: Tue Jun 16, 2009 7:27 pm
by Harnon
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!

Re: Coercing to double-float

Posted: Tue Jun 16, 2009 10:58 pm
by simon
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