
Author 
Message 
Paul G... 
Posted: Sun Sep 05, 2010 6:10 am 



Hi,
I'm using GNU CLISP 2.48. I've caught LISP making a pretty grievous
math error, and I don't know if it's a bug or if there's another
explanation.
I input this line:
(+ 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4)
And I get this result:
32.800003
Obviously this answer is off by .000003. Could somebody explain to me
why this is, and if there's a way to add these numbers correctly using
CLISP?


Back to top 

Teemu Likonen... 
Posted: Sun Sep 05, 2010 10:37 am 



* 20100904 23:10 (0700), Paul G. wrote:
Quote: I'm using GNU CLISP 2.48. I've caught LISP making a pretty grievous
math error, and I don't know if it's a bug or if there's another
explanation.
I input this line:
(+ 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4)
And I get this result:
32.800003
Obviously this answer is off by .000003. Could somebody explain to me
why this is, and if there's a way to add these numbers correctly using
CLISP?
Floating point numbers are not exact; they have limited precision
because their machine implementation uses fixed number of bits. This
feature is not specific to CLISP. See:
"What Every Computer Scientist Should Know About FloatingPoint
Arithmetic"
http://docs.sun.com/source/8063568/ncg_goldberg.html
In Common Lisp you could use ratios if you need exact math:
(+ 1/5 2/5 1/5 1/5 9 4/5 15 2/5 1 1/5 5 2/5)
=> 164/5


Back to top 

Aleksander Nabag³o... 
Posted: Mon Sep 06, 2010 11:27 am 



!
Paul G pisze:
Quote: Hi,
I'm using GNU CLISP 2.48. I've caught LISP making a pretty grievous
math error, and I don't know if it's a bug or if there's another
explanation.
I input this line:
(+ 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4)
And I get this result:
32.800003
Obviously this answer is off by .000003. Could somebody explain to me
why this is, and if there's a way to add these numbers correctly using
CLISP?
;; Dribble of #<IO TERMINALSTREAM> started on NIL.
#<OUTPUT BUFFERED FILESTREAM CHARACTER #P"floatformat.out">
[7]> (quit)
;; Dribble of #<IO TERMINALSTREAM> started on NIL.
#<OUTPUT BUFFERED FILESTREAM CHARACTER #P"floatformat.out">
[2]> (+ 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4)
32.800003
[3]> (setf *readdefaultfloatformat* 'doublefloat)
DOUBLEFLOAT
[4]> (+ 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4)
32.800000000000004
[5]> (setf *readdefaultfloatformat* 'longfloat)
LONGFLOAT
[6]> (+ 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4)
32.800000000000000003
[7]> (setf (ext:longfloatdigits) 70)
70
[8]> (+ 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4)
32.800000000000000000000000001
[9]> (quit)

A
..


Back to top 

kodifik... 
Posted: Mon Sep 06, 2010 3:45 pm 



On Sep 6, 5:25Â pm, Teemu Likonen <tliko... at (no spam) iki.fi> wrote:
Quote: * 20100906 16:54 (+0200), StanisÅ‚aw Halik wrote:
W dniu 20100906 15:39, Teemu Likonen pisze:
you should probably RATIONALIZE the numbers
Or rather, RATIONAL the numbers [sic], since RATIONALIZE is imprecise.
That depends on how we interpret "accuracy" and perhaps also where the
numbers come from. I believe RATIONALIZE is what the original poster
wants here.
Â Â (mapcar #'rationalize '(0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4))
Â Â => (1/5 2/5 1/5 1/5 49/5 77/5 6/5 27/5)
Â Â (mapcar #'rational '(0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4))
Â Â => (13421773/67108864 13421773/33554432 13421773/67108864
Â Â Â Â 13421773/67108864 10276045/1048576 8074035/524288
Â Â Â Â 5033165/4194304 11324621/2097152)
Use of rational can be reasonable however inside a wrapper:
(defun myadd (&rest nums) (float (reduce (function +) (mapcar
(function rational) nums))))
....so that: (myadd 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4) > 32.8


Back to top 

Teemu Likonen... 
Posted: Mon Sep 06, 2010 5:39 pm 



* 20100905 09:37 (+0300), Teemu Likonen wrote:
Quote: * 20100904 23:10 (0700), Paul G. wrote:
I input this line:
(+ 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4)
And I get this result: 32.800003
In Common Lisp you could use ratios if you need exact math:
(+ 1/5 2/5 1/5 1/5 9 4/5 15 2/5 1 1/5 5 2/5)
=> 164/5
If you get those floating point numbers from user or some other source
which you can't control you should probably RATIONALIZE the numbers
before doing any calculations (that is, if floating point numbers are
not accurate enough for you).
(mapcar #'rationalize '(0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4))
=> (1/5 2/5 1/5 1/5 49/5 77/5 6/5 27/5)
(apply #'+ *)
=> 164/5
(float *)
=> 32.8


Back to top 

StanisÅ‚aw Halik... 
Posted: Mon Sep 06, 2010 6:54 pm 



W dniu 20100906 15:39, Teemu Likonen pisze:
you should probably RATIONALIZE the numbers
Or rather, RATIONAL the numbers [sic], since RATIONALIZE is imprecise.


Back to top 

Teemu Likonen... 
Posted: Mon Sep 06, 2010 7:25 pm 



* 20100906 16:54 (+0200), StanisÅ‚aw Halik wrote:
Quote: W dniu 20100906 15:39, Teemu Likonen pisze:
you should probably RATIONALIZE the numbers
Or rather, RATIONAL the numbers [sic], since RATIONALIZE is imprecise.
That depends on how we interpret "accuracy" and perhaps also where the
numbers come from. I believe RATIONALIZE is what the original poster
wants here.
(mapcar #'rationalize '(0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4))
=> (1/5 2/5 1/5 1/5 49/5 77/5 6/5 27/5)
(mapcar #'rational '(0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4))
=> (13421773/67108864 13421773/33554432 13421773/67108864
13421773/67108864 10276045/1048576 8074035/524288
5033165/4194304 11324621/2097152)


Back to top 

Pascal J. Bourguignon... 
Posted: Wed Sep 08, 2010 4:00 am 



seeWebInstead at (no spam) rem.intarweb.org (Robert Maas, http://tinyurl.com/uh3t) writes:
Quote: From: Paul G <pavel... at (no spam) gmail.com
I'm using GNU CLISP 2.48. I've caught LISP making a pretty grievous
math error, and I don't know if it's a bug or if there's another
explanation.
I input this line:
(+ 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4)
which converts each of those decimaldigit expressions into
floatingpoint binary approximations. In particular not one of
those eight values you entered can be expresssed exactly in binary.
(If you don't believe me, try to find an exact binary value that
equals any one of those, and report which exact binary value you
claim exactly equals which of the decimal values there.)
Then seven floatingpointapproximate additions are done before
yielding the grand total, each of those possibly causign additional
roundoff errors. Finally the binary result is converted back to
decimal notation, with additional conversion error, to print the
result. So that's a total of nine (9) times you absolutely cannot
get the correct result so some roundoff *must* occur, and seven (7)
times you might also get roundoff error. Only an idiot would expect
the final result to print exactly how it'd be if you did all the
arithmetic by hand using decimal notation.
In the old languages, there were two kinds of numbers, binary and
decimal. So the 'idiots' (not doing scientific computation) could use
the decimal numbers and get the 'right' answers.
This most often occurs in financial computing, where the units are not
the dollar or euro, but actually the cent, and all amounts are not
floating points or real, but integer numbers of cents.
Quote: And I get this result:
32.800003
(COM.INFORMATIMAGO.COMMONLISP.INVOICE::+ #m0.2 #m0.4 #m0.2 #m0.2 #m9.8 #m15.4 #m1.2 #m5.4)
32.80 EUR
Quote: Could somebody explain to me why this is,
Why do I need to explain it to you? Don't you have the slighest
concept of decimal and binary notational systems, and the wisdom to
know that values in one system generally cannot be expressed
exactly in the other system, and indeed specifically that *none* of
the eight input values you gave above can be expressed exactly in
binary?
When I was in primary school, we learned bases and base conversion
along with decimal arithmetic. But that was a long time ago, I hear
nowadays, they're not able to teach pupils even to count in base
ten...
Quote: and if there's a way to add these numbers correctly using CLISP?
Sure. Write a decimalarithmetic package, which will be several
orders of magnitude slower than machine floatingpoint arithmetic,
but give you exact answers so long as you only add subtract and
multiply (never divide). But why would anybody waste their time
doing that?
It wouldn't be necessarily slower:
 some hardware have decimal integer or decimal floatingpoint support.
 nowadays computers are so fast, that instead of waiting for memory
doing nothing, they could as well spend some time doing decimal
arithmetic meanwhile.
There's no time to waste much time, there are libraries (or chunks of
code to be scrapped). And we would be doing that to avoid spending a
lot of time explaining newbies why (+ 0.2 15.4) is not 15.6, and just
point them to the decimal arithmetic library instead.
Quote: Bottom line: You haven't caught LISP making any math error, but
I've caught you making a pretty grievous error in understanding
what exactly you asked Lisp to calculate for you.

__Pascal Bourguignon__ http://www.informatimago.com/


Back to top 

Paul G... 
Posted: Wed Sep 08, 2010 4:01 am 



Thanks to everyone who replied!
Teemu, Stanislaw, and Kodifik, thanks in particular for showing me how
to do the calculation using rational or rationalize.
Sam, thanks for the link. I tried using google to find an explanation
before posting here, but I didn't find that particular faq.
Robert, you labeled me an idiot because I expected LISP to be able to
perform mathematical operations on par with a pocket calculator. Your
post was informative, but also rude and obnoxious. I suggest you read
up on netiquette. Here's a reference to get you started:
http://www.ietf.org/rfc/rfc1855.txt.
Quote: Bottom line: You haven't caught LISP making any math error, but
I've caught you making a pretty grievous error in understanding
what exactly you asked Lisp to calculate for you.
*Sigh*. Now I know that I'm dealing with a computer scientist, and
not a mathematician. How can you assert that adding a series of
numbers and displaying the wrong result is not a math error? Next
you'll probably try to convince me that there are 1024 meters in a
kilometer.


Back to top 

Pascal J. Bourguignon... 
Posted: Wed Sep 08, 2010 4:04 am 



seeWebInstead at (no spam) rem.intarweb.org (Robert Maas, http://tinyurl.com/uh3t) writes:
Quote: and if there's a way to add these numbers correctly using CLISP?
Sure. Write a decimalarithmetic package, which will be several
orders of magnitude slower than machine floatingpoint arithmetic,
but give you exact answers so long as you only add subtract and
multiply (never divide). But why would anybody waste their time
doing that?
For example, somebody could waste their time doing that to avoid
killing people, since lay programmers are too dumb to do the correct
thing in the first place:
http://www.ima.umn.edu/~arnold/disasters/patriot.html

__Pascal Bourguignon__ http://www.informatimago.com/


Back to top 

Nils M Holm... 
Posted: Wed Sep 08, 2010 5:16 am 



Pascal J. Bourguignon <pjb at (no spam) informatimago.com> wrote:
Quote: seeWebInstead at (no spam) rem.intarweb.org (Robert Maas, http://tinyurl.com/uh3t) writes:
Sure. Write a decimalarithmetic package, which will be several
orders of magnitude slower than machine floatingpoint arithmetic,
but give you exact answers so long as you only add subtract and
multiply (never divide). But why would anybody waste their time
doing that?
For example, somebody could waste their time doing that to avoid
killing people, [...]
Or because there's nothing else to do and drinking is not an option:
http://www.t3x.org/s9fes/index.html
BTW, it uses base1,000,000,000 arithmetics on 32bit machines,
(and base1e18 on 64bit systems) which maps fine to base10 and
it not *that* slow.

Nils M Holm  http://t3x.org


Back to top 

George Neuner... 
Posted: Wed Sep 08, 2010 5:16 am 



On Tue, 07 Sep 2010 15:04:03 0700, seeWebInstead at (no spam) rem.intarweb.org
(Robert Maas, http://tinyurl.com/uh3t) wrote:
Quote: From: Paul G <pavel... at (no spam) gmail.com
and if there's a way to add these numbers correctly using CLISP?
Sure. Write a decimalarithmetic package, which will be several
orders of magnitude slower than machine floatingpoint arithmetic,
but give you exact answers so long as you only add subtract and
multiply (never divide). But why would anybody waste their time
doing that?
On x86 you could put a nice face on Intel's decimal floating point
library:
http://software.intel.com/enus/articles/inteldecimalfloatingpointmathlibrary/
Or work with the latest PowerPCs which have decimal hardware already.
George


Back to top 

Thomas A. Russ... 
Posted: Wed Sep 08, 2010 9:49 pm 



Paul G <pavel84g at (no spam) gmail.com> writes:
Quote: *Sigh*. Now I know that I'm dealing with a computer scientist, and
not a mathematician. How can you assert that adding a series of
numbers and displaying the wrong result is not a math error?
But it isn't displaying the wrong result. It is just not displaying the
result you expect. The problem is actually more like a display error in
not showing the full binary value of the floating point number 0.1, for
example. But for most uses, that would be a lot less friendly than the
engineering decision to show a decimal approximation of the actual
binary floating point number.
Real dyed in the wool computer scientists might actually be happier
showing the binary (or hex?) values instead. But that generally
wouldn't be as friendly.
From the mathematical point of view, one has to realize that the
notation shown for floating point is one of those conventions that you
need to learn. For example, most mathematicians would not look kindly
on people complaining that:
_ _
1.3 + 1.6 is not exactly 1.9 (without the bar).
As a practial matter, I do think it would be a Good Idea(tm) for
programming languages to introduce a new primitive numeric type
"decimal" to be used by most people in place of "float" or "double".
Decimal would be an exact decimal fractional number. It should be
similar to the Decimal class in Java, but accorded primitive status just
like int or double. Actually, while up on my soap box, I would also
suggest that there be an unlimited precision integer called "integer" or
"int" and that the current limited precision varieties be renamed with
their precision such as mod32int or mod64int to remind casual
programmers that they wrap around when they get to big.
At least in lisp
(+ 2000000000 2000000000) => 4000000000
Compare Java's
int s = 2000000000 + 2000000000;
System.out.println(s);
294967296
Now, there's a math error!
Quote: Next
you'll probably try to convince me that there are 1024 meters in a
kilometer.
That would be a kibimeter. ;)

Thomas A. Russ, USC/Information Sciences Institute


Back to top 

Pascal J. Bourguignon... 
Posted: Wed Sep 08, 2010 11:00 pm 



Aleksander NabagÅ‚o <n at (no spam) ap.krakow.pl> writes:
Quote: !
Pascal J. Bourguignon pisze:
In the old languages, there were two kinds of numbers, binary and
decimal. So the 'idiots' (not doing scientific computation) could use
the decimal numbers and get the 'right' answers.
This most often occurs in financial computing, where the units are not
the dollar or euro, but actually the cent, and all amounts are not
floating points or real, but integer numbers of cents.
Yes, they hardly excercise to hide fractions of cents
and finally some of them master to hide millions or billions of dollars.
That's the point. If money wasn't an integer number of cents,
multiplying it by fractionnal tax rates would just add decimals, and
there would be no rounding to do. It's because they're integer, that
rounding has to occur, and a fractionnal remainder has to be dealt
with (legally or illegally).

__Pascal Bourguignon__ http://www.informatimago.com/


Back to top 

Pascal J. Bourguignon... 
Posted: Wed Sep 08, 2010 11:05 pm 



tar at (no spam) sevak.isi.edu (Thomas A. Russ) writes:
Quote: For example, most mathematicians would not look kindly
on people complaining that:
_ _
1.3 + 1.6 is not exactly 1.9 (without the bar).
2.9
Quote: As a practial matter, I do think it would be a Good Idea(tm) for
programming languages to introduce a new primitive numeric type
"decimal" to be used by most people in place of "float" or "double".
Decimal would be an exact decimal fractional number.
Indeed. When I was designing languages I had such a type constructor
(my language didn't have any predefined type, so it would be
plateform neutral. There was even a declaration to indicate what type
would test conditions ("booleans") have to have).
Quote: It should be
similar to the Decimal class in Java, but accorded primitive status just
like int or double. Actually, while up on my soap box, I would also
suggest that there be an unlimited precision integer called "integer" or
"int" and that the current limited precision varieties be renamed with
their precision such as mod32int or mod64int to remind casual
programmers that they wrap around when they get to big.
I would even rename them modulo_4294967296_integer to make the
point clearer, and discourage their use even more.

__Pascal Bourguignon__ http://www.informatimago.com/


Back to top 


All times are GMT
The time now is Thu Aug 21, 2014 10:11 pm

