[plt-scheme] Exact/inexact behavior?

From: Joe Marshall (jrm at ccs.neu.edu)
Date: Mon Dec 1 10:54:16 EST 2003

Matt <mcj4 at kent.ac.uk> writes:

>   For list-related administrative tasks:
>   http://list.cs.brown.edu/mailman/listinfo/plt-scheme
>
>
> Hi all,
>
>
> Welcome to MzScheme version 204, Copyright (c) 1995-2003 PLT
>> (define foo '(0.051 0.042
>                 0.042 0.048
>                 0.042 0.042
>                 1.193 0.042
>                 0.042 0.288
>                 1.762))
>> (apply + foo)
> 3.5940000000000003
>
> Do we get the .0000000000000003 free in both cases? The OS X caclulator
> gives me "3.594". This came up as I was introducing someone to Scheme in
> the context of a little script to add up the cost of calls on their phone
> bill.
>
> Just my 2.0000000000004 cent question for the day,

When you use inexact numbers in scheme, the implementation often
represents them with floating point values.  As you are probably
aware, not every decimal number has an exact floating point
equivalent, so the nearest floating point number is used.

0.051 ->  7349874591868649/144115188075855872
0.042 ->  6052837899185947/144115188075855872
0.048 ->  3458764513820541/72057594037927936
1.193 ->  2686397177726501/2251799813685248
0.288 ->  5188146770730811/18014398509481984
1.762 ->  3967671271713407/2251799813685248

When you add these numbers together, the answer you get may depend on
the order of the additions because the partial sums may not be
exactly representable with floating point numbers.  For instance:

(foldl + 0.0 foo) => 3.5940000000000003
(foldr + 0.0 foo) => 3.593999999999999

(+ (+ 0.051 0.042 0.042 0.042 0.042 0.042 0.042)
    0.048 
   (+  1.762 1.193)
    0.288)  =>  3.594

The best way to get the exact answer is to use exact arithmetic.
Remember that 0.051 is the same thing as 51/1000, so

 (define foo '(0051 0042
                 0042 0048
                 0042 0042
                 1193 0042
                 0042 0288
                 1762))

(define foo1 (map (lambda (x) (/ x 1000)) foo))

(foldl + 0 foo1) =>  1797/500

(exact->inexact 1797/500) => 3.594




Posted on the users mailing list.