[comp.lang.c] floating point multiplication BUG in C

mishra@banach.ACA.MCC.COM (Aditya Mishra) (10/20/90)

BUG !!!   BUG !!!   BUG !!!

THIS PROGRAM DEMONSTRATES WHAT I BELIEVE IS A BUG IN FLOATING POINT
MULTIPLICATION IN 'C' !! 

I ACCIDENTALLY STUMBLED ON A PATTERN OF
FLOATING POINT OPERAND VALUES FOR WHICH RESULTS ARE QUITE INACCURATE !!!

I WAS GETTING UNDESIRED BEHAVIOR IN A PROGRAM BECAUSE OF THIS (APPARENT) BUG.

I FELT IT MIGHT BE SERIOUS AND IMPORTANT TO ALL C USERS.

 
bug.c
-----

Aditya Mishra, MCC Austin  (512) 338 3481
-----------------------------------------

(1)   In the program, if float f = ... 0.64, 0.32, 0.16, 0.08, 0.04, 0.02, 
	0.01, 0.005, 0.0025, ..., etc (see the pattern?), the value 
	of i1 was one less than what it should be. 
      This error was absent if the value of f was not 
        from the above pattern (if f = 0.65, e. g.).

(2)   Also, if float g = 1.0025, 1.32 or 9.0025 and some more values, 
        the result in float r2
	(r2 = factor * g) was wrong after 2 or 3 decimal places.

(3)   These bugs persist on cc (C compiler) on about six machines 
	that I could get my hands on.

(4)   I am not sure if this is really a bug. I am just expressing an
        experience that teaches me to be careful with floating point
        results of multiplication.
 
*/

/*--	--	--	--	--	--	--*/

#include <stdio.h>

main()
{
	int i1, i2;
	float r1, r2;

	float g = 9.0025;
	float f = 0.32;

	float factor = 10000.0;
 	
	i1 = factor * f; /* *even if*  i = (int) (factor * f); */
	r1 = factor * f;

	i2 = factor * g; /* *even if*  i = (int) (factor * g); */
	r2 = factor * g;
	
	printf("%d   ", i1);
	printf("%f \n", r1);

	printf("%d   ", i2);
	printf("%f \n", r2);
}

/*--	--	--	--	--	--	--*/

kdq@demott.COM (Kevin D. Quitt) (10/20/90)

In article <1348@banach.ACA.MCC.COM> mishra@banach.ACA.MCC.COM (Aditya Mishra) writes:
>BUG !!!   BUG !!!   BUG !!!
>
>THIS PROGRAM DEMONSTRATES WHAT I BELIEVE IS A BUG IN FLOATING POINT
>MULTIPLICATION IN 'C' !! 

    Calm down, take a deep breath.  There. Feel better?

    This has nothing to do with C.  It has to do with your individual
compiler (which, by the way you didn't identify) on your particular
machine (which you also didn't identify), the libraries used, and quite
possibly the floating-point notation itself. 

    cc on this machine also gets 3199 instead of 3200, but gnu's gcc gets
this one right.  They both miss the second one by one, so it's possible
that this is due to the inherent inaccuracy of floating point numbers.  If
your need to be exactly on, use integers, not floating point, or round the
data in the process of conversion to integer - remember: conversion from
floating point to integer in C is defined as truncation, not rounding.

    If the floating point value is off by one LSB (as it's allowed to be in
the IEEE spec), you will not get the correct integer.  Please note the
following warning generated by the Microsoft C compiler:

foo.c(13) : warning C4051: type conversion - possible loss of data

    See what it's trying to tell you?  So again, relaxen und vatchen das
blinkenlites.
-- 
 _
Kevin D. Quitt         demott!kdq   kdq@demott.com
DeMott Electronics Co. 14707 Keswick St.   Van Nuys, CA 91405-1266
VOICE (818) 988-4975   FAX (818) 997-1190  MODEM (818) 997-4496 PEP last

                96.37% of all statistics are made up.

henry@zoo.toronto.edu (Henry Spencer) (10/21/90)

In article <1348@banach.ACA.MCC.COM> mishra@banach.ACA.MCC.COM (Aditya Mishra) writes:
>THIS PROGRAM DEMONSTRATES WHAT I BELIEVE IS A BUG IN FLOATING POINT
>MULTIPLICATION IN 'C' !! 

There is no bug.  You have discovered the joys of floating-point arithmetic.

>(1)   In the program, if float f = ... 0.64, 0.32, 0.16, 0.08, 0.04, 0.02, 
>	0.01, 0.005, 0.0025, ..., etc (see the pattern?), the value 
>	of i1 was one less than what it should be. 

Note that float->int conversion truncates, while %f conversion rounds.
You have come across boundary cases where this makes a difference, I'd
say.  Look hard and you will probably find more.

>(2)   Also, if float g = 1.0025, 1.32 or 9.0025 and some more values, 
>        the result in float r2
>	(r2 = factor * g) was wrong after 2 or 3 decimal places.

Wrong after the decimal point, you mean?  If so, no surprise:  `float',
as opposed to `double', often only has about seven digits of precision.
In general, you should use `double' for all floating-point arithmetic
you care about, unless speed or storage is critical and you have done
a careful analysis of the tradeoffs involved.
-- 
The type syntax for C is essentially   | Henry Spencer at U of Toronto Zoology
unparsable.             --Rob Pike     |  henry@zoo.toronto.edu   utzoo!henry

dan@kfw.COM (Dan Mick) (10/21/90)

In article <1348@banach.ACA.MCC.COM> mishra@banach.ACA.MCC.COM (Aditya Mishra) writes:
>BUG !!!   BUG !!!   BUG !!!
>
>THIS PROGRAM DEMONSTRATES WHAT I BELIEVE IS A BUG IN FLOATING POINT
>MULTIPLICATION IN 'C' !! 

Just out of curiosity:  Why do you think this is a problem with C?

Did it ever occur to you that it might be a problem with floating point 
on the machine?  Did you try it in Pascal, or assembly, or FORTRAN, or
as a thought experiment?

True, the problems with floating-point math on computers *do* take some time
and experience to find, if you've only done string and integer programs before,
but have you never tried to add 0.99 and 0.01 on any machine, in any language,
before?

It's not C's fault.

And, by the way:  Do you know why it's against the law to yell "FIRE!" in
a crowded theater?

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (10/22/90)

In article <1348@banach.ACA.MCC.COM>, mishra@banach.ACA.MCC.COM (Aditya Mishra) writes:
> BUG !!!   BUG !!!   BUG !!!
> THIS PROGRAM DEMONSTRATES WHAT I BELIEVE IS A BUG IN FLOATING POINT
> MULTIPLICATION IN 'C' !! 

Please take the trouble to learn something about floating point arithmetic
before screaming to the world that the sky is falling.

> (1)   In the program, if float f = ... 0.64, 0.32, 0.16, 0.08, 0.04, 0.02, 
> 	0.01, 0.005, 0.0025, ..., etc (see the pattern?), the value 
> 	of i1 was one less than what it should be. 
[where i1 is calculated as
	float f = /* as above */;
	float factor = 10000.0;
	int i1 = factor * f;
]

The problem here is that a number like 0.64 *CANNOT* *POSSIBLY* be
represented exactly in (finite-precision) floating-point.  At all.
In *any* programming language.  Any compiler, any string->fp converter,
is going to have to round nearly every number there is (only numbers
which are an integer times a power of 2 are likely to be exact); some
of them will round up and some of them will round down.  If it rounds
down, i1 will be 1 less than you expected.  If it rounds up, because
floating pointer->integer conversion truncates towards zero, the error
will be zapped away by the conversion.

2nd year CS students here know about this...

One of the things I really like about C is that unlike Fortran and
Pascal, it doesn't lie to the user.  It doesn't make a false claim
to support "real" arithmetic.  It honestly and openly admits to
'float'.  Believe it:  what you get is floating-point, not real.

-- 
Fear most of all to be in error.	-- Kierkegaard, quoting Socrates.

jfc@athena.mit.edu (John F Carr) (10/22/90)

In article <1990Oct20.231726.3249@zoo.toronto.edu>
	henry@zoo.toronto.edu (Henry Spencer) writes:
>In general, you should use `double' for all floating-point arithmetic
>you care about, unless speed or storage is critical and you have done
>a careful analysis of the tradeoffs involved.

On some newer machines, double is faster.  2 examples: the 68881 (used
with the 680x0 and the IBM RT), and the IBM RS 6000.  This is because
the hardware does only high precision calculations, and must take extra
cycles to round to single precision if that is what you ask for by using
type float instead of double.

--
    John Carr (jfc@athena.mit.edu)

richard@iesd.auc.dk (Richard Flamsholt S0rensen) (10/22/90)

In article <4032@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
> The problem here is that a number like 0.64 *CANNOT* *POSSIBLY* be
> represented exactly in (finite-precision) floating-point.  At all.
> In *any* programming language. [...]

  At least when using *binary* logic as your hardware  ;-)

--
/Richard Flamsholt
richard@iesd.auc.dk

johnb@srchtec.UUCP (John Baldwin) (10/23/90)

>And, by the way:  Do you know why it's against the law to yell "FIRE!" in
>a crowded theater?

I dunno; is it the same reason they have statutes against yelling "MOVIE!"
in a crowded fire-house?

[sorry, i couldn't resist!  ;-} ]

-- 
John T. Baldwin                     | "Pereant qui ante nos nostra dixerunt!"
Search Technology, Inc.             | (A plague on those who said our good
johnb%srchtec.uucp@mathcs.emory.edu |  things before we did!)

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (10/23/90)

In article <4032@goanna.cs.rmit.oz.au> I wrote:
> The problem here is that a number like 0.64 *CANNOT* *POSSIBLY* be
> represented exactly in (finite-precision) floating-point.  At all.
> In *any* programming language. [...]

In article <RICHARD.90Oct22112830@lambda.iesd.auc.dk>,
richard@iesd.auc.dk (Richard Flamsholt S0rensen) replied:
>   At least when using *binary* logic as your hardware  ;-)

I think he may have meant binary floating-point, and at that he
may have meant "radix-2 floating-point", or in fact "floating-
point with radix not a power of 10".

I had some mail by people who hadn't paid any attention to the
context.  The context was that the original poster was upset because
his program hadn't worked on six compiler/hardware combinations.
In that context, _only_ the floating-point systems actually available
to that poster are of relevance:  a radix-10 floating-point system
with 99 digits in the significand, whatever its merits, is of no
interest because not available to that poster.

I also note that 2 is distinctly better than other bases for floating-
point arithmetic.  I'm not just referring to the hidden bit (it's hard
to "hide" more than one bit...), I'm referring to the law
	X (<) Y --> X (<=) {X (+) Y}(/)2 (<=) Y
which bisection relies on.

-- 
Fear most of all to be in error.	-- Kierkegaard, quoting Socrates.

userAKDU@mts.ucs.UAlberta.CA (Al Dunbar) (10/23/90)

In article <1990Oct20.235102.15882@kfw.COM>, dan@kfw.COM (Dan Mick) writes:
>In article <1348@banach.ACA.MCC.COM> mishra@banach.ACA.MCC.COM (Aditya Mishra) writes:
>>BUG !!!   BUG !!!   BUG !!!
>>
>>THIS PROGRAM DEMONSTRATES WHAT I BELIEVE IS A BUG IN FLOATING POINT
>>MULTIPLICATION IN 'C' !!
>
>Just out of curiosity:  Why do you think this is a problem with C?
> <<<stuff deleted>>>
 
Hear, hear. Floating point precision seems to be a sticky topic
these days. When people expect 4.4 and they get 4.3999987 they
somehow think the answer is only good to one digit! The precision
of the result of a floating point calculation doesn't relate to
the number of digits of agreement, but to its numerical proximity
to the correct answer. I always thought it misleading to consider
(pardon, but my Fortran background is showing) REAL*4 (pardon,
float) to be "accurate to 6 or seven digits", as such statements
tend to propagate such "imprecise" beliefs.
 
-------------------+-------------------------------------------
Al Dunbar          |
Edmonton, Alberta  |   this space for rent
CANADA             |
-------------------+-------------------------------------------

dbrooks@osf.org (David Brooks) (10/24/90)

In article <4032@goanna.cs.rmit.oz.au>, ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
|> 
|> The problem here is that a number like 0.64 *CANNOT* *POSSIBLY* be
|> represented exactly in (finite-precision) floating-point.  At all.
|> In *any* programming language.  Any compiler, any string->fp converter,
|> is going to have to round nearly every number there is (only numbers
|> which are an integer times a power of 2 are likely to be exact); some
|> of them will round up and some of them will round down.

Unless, of course, you are blessed with hardware that uses base 10 for
its floating point.  You are assuming as much about the internal
representation of floating numbers as the original poster was.

Are there any such machines around these days?
-- 
David Brooks				dbrooks@osf.org
Systems Engineering, OSF		uunet!osf.org!dbrooks
That last signature was getting a little tired...

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (10/24/90)

In article <1990Oct23.191109@osf.org>, dbrooks@osf.org (David Brooks) writes:
: In article <4032@goanna.cs.rmit.oz.au>, ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:
: |> The problem here is that a number like 0.64 *CANNOT* *POSSIBLY* be
: |> represented exactly in (finite-precision) floating-point.  At all.
: |> In *any* programming language.  Any compiler, any string->fp converter,
: |> is going to have to round nearly every number there is (only numbers
: |> which are an integer times a power of 2 are likely to be exact); some
: |> of them will round up and some of them will round down.

: Unless, of course, you are blessed with hardware that uses base 10 for
: its floating point.  You are assuming as much about the internal
: representation of floating numbers as the original poster was.

Yes of *course* I was, because I was responding to the original poster.
It's *his* hardware that counts, not some other hardware.
It's his *software* that counts, too, not some other software.
The original poster was complaining about what he thought was a bug
in the C compilers available to him on the machines available to him.
Those are the compiler and machine combinations that count when
responding.  If he had told us exactly which machines he was using,
I'd have responded in terms of the FP formats of those specific machines.
That's what he needed to know about.  How would it help him to hear that
some other kind of machine would do the job (B1800, and I think the
B4800 as well, provided the program was rewritten in Fortran or Cobol)?

I am amazed at the number of E-letters I've received from people who
took it for granted that because I responded to the original poster
in terms relevant to his query that I am ignorant of bignums, rationals,
decimal floats, bigfloats, and the like.  In each case I have implemented
or been associated with the implementation of such things.  They are of
little interest to Joe Beginner trying to figure out how to use 'float'
with C compiler X on machine Y.  (A C++ programmer, now that's a
different story.)

-- 
Fear most of all to be in error.	-- Kierkegaard, quoting Socrates.

bengsig@oracle.nl (Bjorn Engsig) (10/24/90)

Article <1990Oct23.191109@osf.org> by dbrooks@osf.org (David Brooks) says:
|Unless, of course, you are blessed with hardware that uses base 10 for
|its floating point.
|
|Are there any such machines around these days?
Lots of them.  Unfortunately, these handhelds don't run C :-)
-- 
Bjorn Engsig,         E-mail: bengsig@oracle.com, bengsig@oracle.nl
ORACLE Corporation    From IBM: auschs!ibmaus!cs.utexas.edu!uunet!oracle!bengsig

            "Stepping in others footsteps, doesn't bring you ahead"