[Haskell-cafe] rounding errors with real numbers.

Matthias Fischmann fis at wiwi.hu-berlin.de
Sun Feb 26 07:33:42 EST 2006



hi,

I think this is the well-known issue of using real numbers in decimal
representation on a machine that thinks binary, but I don't know what
to do with it, and some of you maybe do.

I want to shift+stretch a list of doubles into a given interval.
example:

| x1 = [2, 3, 4, 5, 10]
| y1 = normInterval x1 0 1
| => y1 = [0.0,0.125,0.25,0.375,1.0]

The function that does this looks something like this:

| normInterval :: [Double] -> Double -> Double -> [Double]
| normInterval ps lower upper = map (\ x -> (x - oldLower) * stretch + lower) ps
|     where
|     oldLower = head ps 
|     oldUpper = last ps
|     stretch = (upper - lower) / (oldUpper - oldLower)

However, with bigger numbers I get rounding errors:

| x2 = [0.0,1.9569471624266144e-3,5.870841487279843e-3,1.5655577299412915e-2,3.913894324853229e-2,9.393346379647749e-2,0.2191780821917808,0.5009784735812133,1.1272015655577299,2.504892367906066]
| y2 = normInterval x2 0 1
| => y2 = [0.0,7.8125e-4,2.3437500000000003e-3,6.25e-3,1.5625000000000003e-2,3.7500000000000006e-2,8.750000000000001e-2,0.2,0.45000000000000007,0.9999999999999999]

The solution that pops to my mind is very simple:

| normInterval :: [Double] -> Double -> Double -> [Double]
| normInterval ps lower upper = repair (map (\ x -> (x - oldLower) * stretch + lower) ps)
|     where
|     oldLower = head ps
|     oldUpper = last ps
|     stretch = (upper - lower) / (oldUpper - oldLower)
| 
|     -- fix rounding error:
|     repair [i] = [upper]
|     repair (h:t) = h : repair t

It works, but it's ugly.  Is there a better way to do this?



Thanks,
Matthias
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
Url : http://www.haskell.org//pipermail/haskell-cafe/attachments/20060226/8fe3b2ba/attachment.bin


More information about the Haskell-Cafe mailing list