Re: Floating Point Approximations.

From: David Cressey <cressey73_at_verizon.net>
Date: Thu, 29 Mar 2007 08:42:36 GMT
Message-ID: <0_KOh.9308$fA2.6908_at_trndny02>


"paul c" <toledobythesea_at_oohay.ac> wrote in message news:fSBOh.84735$zU1.64713_at_pd7urf1no...
> Bob Badour wrote:
> > David Cressey wrote:
> >
> >> Here's another example of floating point errors, that's even simpler
> >> than
> >> the 21200 plus 2.5% example.
> >>
> >> int i;
> >> float x = 0;
> >> for (i = 1; i < 100; ++i)
> >> x += 0.01;
> >> x -= 1;
> >> printf (x);
> >>
> >> I apologize if I copied the code wrong. The idea is to start with
zero,
> >> add one hundredth a hundred times, and subtract one. The answer is not
> >> quite zero.
> >>
> >> Many environments will give a small error. This is such a trivial
> >> example
> >> that it should make all programmers shudder about using floats, unless
> >> they
> >> really know what they are doing.
> >
> >
> > The code above only adds 99 times.
> >
> > I translated the above into C, corrected it and expanded on it. The
> > naive method below ignores rounding. The aware method is aware of both
> > the limitation in the representations and in the range of interest
> > (chosen as 5 decimal places based on the useful precision of the float
> > type.)
> >
> > This is such a trivial example that it should make all programmers who
> > don't know what they are doing shudder.
> >
> > ------------>8----------------------->8-----------------
> >
> > #include <math.h>
> > #include <stdio.h>
> >
> > int main() {
> > int i;
> > float x = 0;
> > double d = 0;
> >
> > /* naive method */
> > printf("naive\n");
> > for (i = 0; i < 100; ++i)
> > x += 0.01;
> > x -= 1;
> > printf ("%8.5f %e\n",x,x);
> >
> > for (i = 0; i < 100; ++i)
> > d += 0.01;
> > d -= 1;
> > printf ("%8.5f %e\n",d,d);
> >
> > x = 0.01;
> > x *= 100;
> > x -= 1;
> > printf("%8.5f %e\n",x,x);
> >
> > d = 0.01;
> > d *= 100;
> > d -= 1;
> > printf("%8.5f %e\n",d,d);
> >
> > x = 0;
> > for (i = 1; i < 100; ++i)
> > x += 0.01;
> > x -= 0.99;
> > printf ("%8.5f %e\n",x,x);
> >
> > d = 0;
> > for (i = 1; i < 100; ++i)
> > d += 0.01;
> > d -= 0.99;
> > printf ("%8.5f %e\n",d,d);
> >
> > x = 0.99;
> > d = 0.99;
> > x += 0.01;
> > d += 0.01;
> >
> > x -= 1;
> > printf("%8.5f %e\n",x,x);
> >
> > d -= 1;
> > printf("%8.5f %e\n",d,d);
> >
> >
> > /* aware method */
> > printf("aware\n");
> > for (i = 0; i < 100; ++i)
> > x += 0.01;
> > x -= 1;
> > printf ("%8.5f %e\n",floor(x*1e5+0.5)/1e5,floor(x*1e5+0.5)/1e5);
> >
> > for (i = 0; i < 100; ++i)
> > d += 0.01;
> > d -= 1;
> > printf ("%8.5f %e\n",floor(d*1e5+0.5)/1e5,floor(d*1e5+0.5)/1e5);
> >
> > x = 0.01;
> > x *= 100;
> > x -= 1;
> > printf ("%8.5f %e\n",floor(x*1e5+0.5)/1e5,floor(x*1e5+0.5)/1e5);
> >
> > d = 0.01;
> > d *= 100;
> > d -= 1;
> > printf ("%8.5f %e\n",floor(d*1e5+0.5)/1e5,floor(d*1e5+0.5)/1e5);
> >
> > x = 0;
> > for (i = 1; i < 100; ++i)
> > x += 0.01;
> > x -= 0.99;
> > printf ("%8.5f %e\n",floor(x*1e5+0.5)/1e5,floor(x*1e5+0.5)/1e5);
> >
> > d = 0;
> > for (i = 1; i < 100; ++i)
> > d += 0.01;
> > d -= 0.99;
> > printf ("%8.5f %e\n",floor(d*1e5+0.5)/1e5,floor(d*1e5+0.5)/1e5);
> >
> > x = 0.99;
> > d = 0.99;
> > x += 0.01;
> > d += 0.01;
> >
> > x -= 1;
> > printf ("%8.5f %e\n",floor(x*1e5+0.5)/1e5,floor(x*1e5+0.5)/1e5);
> >
> > d -= 1;
> > printf ("%8.5f %e\n",floor(d*1e5+0.5)/1e5,floor(d*1e5+0.5)/1e5);
> >
> > return 0;
> > }

>

> No argument with any of the above. I notice neither post mentioned
> whether the floats were binary or decimal. I wonder how the above might
> change if run with compiler and hardware that supported decimal floats?
>

Paul C,

The book I was copying from (The user's guide for Turbo C++) was making the point for using, in certain circumstances, BCD math instead of binary arithmetic. If you declare X to be of type bcd, instead of float, you get the exact number 1.00 when you add a hundred pennies.

Thanks for raising this point, as it's really basic to the topic I was intending to discuss.

> (Don't want to change the point, as I think it is basically a crime or
> at least irresponsible bordering on the criminal for engineers to design
> the machines consumers use with such a lack of decimal precision. I
> think hardware engineers generally live in a higher ivory tower than
> even the OO software people.)
>

Two points on BCD math:

One, I have a dim recollection of looking at the 8086 instruction set, back in 1982, and seeing some instructions that look like they would be good for speeding up BCD math. Note that I'm not talking about the 8087 math co-processor.

Second, I think Oracle's "number" type uses BCD math, and not binary floating point.
I don't have Oracle handy, but I plan on trying out adding up hundred pennies to see if I get a dollar, the next time I'm at a computer with Oracle.

In fact, if I recall correctly, this was one source of small but annoying discrepancies between code doing arithmetic on decimal fractions in Oracle and in DEC Rdb/VMS. For a while, I had to move back and forth between the two.

> IBM would be doing a public service to put Cowlishaw's decimal code in
> the public domain. Don't understand why they don't.
>
Don't know this one.

> Of course, if we eliminated programmers, then the users would be
> responsible for the above results, just as they are when they make a
> spreadsheet and there might be less shuddering!
>

Unless the spreadsheet uses binary floating point arithmetic, which I hope they don't.
However, even if the spreadsheet uses BCD math, the user who makes an amortization schedule for a mortgage on a spreadsheet will very quickly find themselves dealing with the problem of approximations.

Likewise the programmer who uses Oracle math for amortization calculations.

Regardless of whether you're a programmer or a user, this problem is going to surface as soon as you start using a computer for arithmetic. Of course some people think that using a computer for arithmetic is as quaint as using duct tape to seal ducts. On The Red Green show, one guy expressed amazement that duct tape can be used for that, too.

Anyway, Paul, you and I are on the same page on this issue. Ordinary commerce should use exact representations for decimal fractions. Received on Thu Mar 29 2007 - 10:42:36 CEST

Original text of this message