Main Page | Report Page

 

  Computers Forum Index » Computer Languages (Lisp) » Is this a bug in LISP?...

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?
 
Teemu Likonen...
Posted: Sun Sep 05, 2010 10:37 am
 
* 2010-09-04 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 Floating-Point
Arithmetic"

http://docs.sun.com/source/806-3568/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
 
Aleksander Nabago...
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 TERMINAL-STREAM> started on NIL.

#<OUTPUT BUFFERED FILE-STREAM CHARACTER #P"float-format.out">
[7]> (quit)
;; Dribble of #<IO TERMINAL-STREAM> started on NIL.

#<OUTPUT BUFFERED FILE-STREAM CHARACTER #P"float-format.out">
[2]> (+ 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4)
32.800003
[3]> (setf *read-default-float-format* 'double-float)
DOUBLE-FLOAT
[4]> (+ 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4)
32.800000000000004
[5]> (setf *read-default-float-format* 'long-float)
LONG-FLOAT
[6]> (+ 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4)
32.800000000000000003
[7]> (setf (ext:long-float-digits) 70)
70
[8]> (+ 0.2 0.4 0.2 0.2 9.8 15.4 1.2 5.4)
32.800000000000000000000000001
[9]> (quit)


--
A
..
 
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:
* 2010-09-06 16:54 (+0200), Stanisław Halik wrote:

W dniu 2010-09-06 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
 
Teemu Likonen...
Posted: Mon Sep 06, 2010 5:39 pm
 
* 2010-09-05 09:37 (+0300), Teemu Likonen wrote:

Quote:
* 2010-09-04 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
 
Stanisław Halik...
Posted: Mon Sep 06, 2010 6:54 pm
 
W dniu 2010-09-06 15:39, Teemu Likonen pisze:
you should probably RATIONALIZE the numbers

Or rather, RATIONAL the numbers [sic], since RATIONALIZE is imprecise.
 
Teemu Likonen...
Posted: Mon Sep 06, 2010 7:25 pm
 
* 2010-09-06 16:54 (+0200), Stanisław Halik wrote:

Quote:
W dniu 2010-09-06 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)
 
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 decimal-digit expressions into
floating-point 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 floating-point-approximate 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.COMMON-LISP.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 decimal-arithmetic package, which will be several
orders of magnitude slower than machine floating-point 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 floating-point 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/
 
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.
 
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 decimal-arithmetic package, which will be several
orders of magnitude slower than machine floating-point 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/
 
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 decimal-arithmetic package, which will be several
orders of magnitude slower than machine floating-point 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 base-1,000,000,000 arithmetics on 32-bit machines,
(and base-1e18 on 64-bit systems) which maps fine to base-10 and
it not *that* slow.

--
Nils M Holm | http://t3x.org
 
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 decimal-arithmetic package, which will be several
orders of magnitude slower than machine floating-point 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/en-us/articles/intel-decimal-floating-point-math-library/


Or work with the latest PowerPCs which have decimal hardware already.

George
 
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
 
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/
 
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 pre-defined 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/
 
 
Page 1 of 4    Goto page 1, 2, 3, 4  Next
All times are GMT
The time now is Thu Apr 24, 2014 8:15 pm