By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
434,677 Members | 1,136 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 434,677 IT Pros & Developers. It's quick & easy.

Weird arithmetic

P: n/a
Hi newsgroup,

I am new to C and wonder about the following code:

#include <stdio.h>

int main(void)
{
double m = 100.45;
double n = 200.45;
double o = 200.45;

int i = m * 100;
int j = n * 100;

o = n * 100;
int k = o;

printf("%lf = %i\n"
"%lf = %i\n"
"%lf = %i\n", m * 100, i, n * 100, j, o, k);

return 0;
}
Compiled with GCC-3.4.3 on a Linux box (Fedora Core 3 x86) the output
looks like this:

$ gcc -Wall -pedantic -std=c99 test.c -o test
$ ./test
10045.000000 = 10045
20045.000000 = 20044
20045.000000 = 20045

How could this be?

Thanks in advance!
- Pascal

Nov 14 '05 #1
Share this Question
Share on Google+
20 Replies


P: n/a

"Pascal Gallois" <pg@nowhere.net> wrote in message
news:pa****************************@nowhere.net...
Hi newsgroup,

I am new to C and wonder about the following code:

#include <stdio.h>

int main(void)
{
double m = 100.45;
double n = 200.45;
double o = 200.45;

int i = m * 100;
int j = n * 100;

o = n * 100;
int k = o;

printf("%lf = %i\n"
"%lf = %i\n"
"%lf = %i\n", m * 100, i, n * 100, j, o, k);

return 0;
}
Compiled with GCC-3.4.3 on a Linux box (Fedora Core 3 x86) the output
looks like this:

$ gcc -Wall -pedantic -std=c99 test.c -o test
$ ./test
10045.000000 = 10045
20045.000000 = 20044
20045.000000 = 20045

How could this be?


http://tinyurl.com/b5ms6

-Mike
Nov 14 '05 #2

P: n/a
Pascal Gallois wrote:

Hi newsgroup,

I am new to C and wonder about the following code:

#include <stdio.h>

int main(void)
{
double m = 100.45;
double n = 200.45;
double o = 200.45;

int i = m * 100;
int j = n * 100;

o = n * 100;
int k = o;

printf("%lf = %i\n"
"%lf = %i\n"
"%lf = %i\n", m * 100, i, n * 100, j, o, k);

return 0;
}

Compiled with GCC-3.4.3 on a Linux box (Fedora Core 3 x86) the output
looks like this:

$ gcc -Wall -pedantic -std=c99 test.c -o test
$ ./test
10045.000000 = 10045
20045.000000 = 20044
20045.000000 = 20045

How could this be?


Rounding. Remember, not all decimal numbers can be stored exactly as
binary numbers.

Change the printf as follows to see more details:

printf("%30.20lf %.20lf = %i\n"
"%30.20lf %.20lf = %i\n"
"%30.20lf %.20lf = %i\n",
m, m * 100, i,
n, n * 100, j,
n, o, k);

On my system, "100.45" is "100.45000000000000284217", whereas "200.45"
is "200.44999999999998863132". With the multiplication and conversion
to integer, your system may cause the second to truncate to "20044".

Also, note that my system gives "20045" for the second line of output.

--
+-------------------------+--------------------+-----------------------------+
| Kenneth J. Brody | www.hvcomputer.com | |
| kenbrody/at\spamcop.net | www.fptech.com | #include <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------------+
Don't e-mail me at: <mailto:Th*************@gmail.com>

Nov 14 '05 #3

P: n/a
On Wed, 18 May 2005 13:27:35 -0400, Kenneth Brody wrote:
Rounding. Remember, not all decimal numbers can be stored exactly as
binary numbers.

Change the printf as follows to see more details:

printf("%30.20lf %.20lf = %i\n"
"%30.20lf %.20lf = %i\n"
"%30.20lf %.20lf = %i\n",
m, m * 100, i,
n, n * 100, j,
n, o, k);

On my system, "100.45" is "100.45000000000000284217", whereas "200.45"
is "200.44999999999998863132". With the multiplication and conversion
to integer, your system may cause the second to truncate to "20044".

Also, note that my system gives "20045" for the second line of output.


Thanks Kenneth,

I applied your suggested version and as you said, the initial values of
m, n and o are not exact. But after the multiplication the output
indicates that they are (or at least I interpret the fractional 0s this
way):

$ ./test
100.45000000000000284217 10045.00000000000000000000 = 10045
200.44999999999998863132 20045.00000000000000000000 = 20044
200.44999999999998863132 20045.00000000000000000000 = 20045

Shouldn't the conversion from double to int just discard the fractional
part? I guess I misinterpret something here but I still don't get it
completely. Do you care to explain to me?

- Pascal
Nov 14 '05 #4

P: n/a
In article <pa****************************@nowhere.net>,
Pascal Gallois <pg@nowhere.net> wrote:
Shouldn't the conversion from double to int just discard the fractional
part?


No, there are elaborate rules about what should be rounded in what
direction. If you follow that tinyurl that was pointed, have a look
at the "round to even" portion.
--
"[...] it's all part of one's right to be publicly stupid." -- Dave Smey
Nov 14 '05 #5

P: n/a
On Wed, 18 May 2005 17:57:22 +0000, Walter Roberson wrote:
In article <pa****************************@nowhere.net>,
Pascal Gallois <pg@nowhere.net> wrote:
Shouldn't the conversion from double to int just discard the fractional
part?


No, there are elaborate rules about what should be rounded in what
direction. If you follow that tinyurl that was pointed, have a look
at the "round to even" portion.


Hhhmm. Quoting from ISO/IEC 9899:1999:

"The conversions from floating to integer types provide IEC 60559-like conversions
but always round toward zero." (F.3)

and:

"When a finite value of real floating type is converted to an integer type other than _Bool,
the fractional part is discarded (i.e., the value is truncated toward
zero)." (6.3.1.4)

So I understand that the computation involving floating point values might
produce rounding errors. Lets say after double m = 200.45; m is really
200.44999999999998863132. But why does printf("%.30lf\n", m * 100);
print 20045.000000000000000000000000000000 which would be 20045 if
assigned to an int, but gets 20044 when assigned? Shouldn't printf show
something like 20044.9999999999988... in this case?

Thank you!
- Pascal
Nov 14 '05 #6

P: n/a
Pascal Gallois <pg@nowhere.net> writes:
Hi newsgroup,

I am new to C and wonder about the following code:
[snip]
Compiled with GCC-3.4.3 on a Linux box (Fedora Core 3 x86) the output
looks like this:

$ gcc -Wall -pedantic -std=c99 test.c -o test
$ ./test
10045.000000 = 10045
20045.000000 = 20044
20045.000000 = 20045

How could this be?


Here's another program that exhibits the problem:

#include <stdio.h>

int main(void)
{
double x = 200.45;
double x_times_100 = x * 100;

printf("x = %46.40f --> %d\n", x, (int)x);
printf("x * 100 = %46.40f --> %d\n", x * 100, (int)(x * 100));
printf("x_times_100 = %46.40f --> %d\n", x_times_100, (int)x_times_100);

return 0;
}

On x86 systems, I get the following output:

x = 200.4499999999999886313162278383970260620117 --> 200
x * 100 = 20045.0000000000000000000000000000000000000000 --> 20044
x_times_100 = 20045.0000000000000000000000000000000000000000 --> 20045

On a SPARC system, I get:

x = 200.4499999999999886313162278383970260620117 --> 200
x * 100 = 20045.0000000000000000000000000000000000000000 --> 20045
x_times_100 = 20045.0000000000000000000000000000000000000000 --> 20045

There's no guarantee that 200.45 * 100 >= 20045.0 -- but if it is, the
result of converting 20045.0 to int should be 20045, not 20044.

I think there's an issue on x86 systems with intermediate results
being stored in extended-precision registers. It looks like the
result of x*100 is being stored with extra precision when it's passed
directly to printf(), but not when it's the operand of a cast to int.
Or something like that.

The "-ffloat-store" option didn't affect the behavior.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #7

P: n/a
Pascal Gallois wrote:
Hi newsgroup,

I am new to C and wonder about the following code:

#include <stdio.h>

int main(void)
{
double m = 100.45;
double n = 200.45;
double o = 200.45;

int i = m * 100;
int j = n * 100;

o = n * 100;
int k = o;

printf("%lf = %i\n"
"%lf = %i\n"
"%lf = %i\n", m * 100, i, n * 100, j, o, k);

return 0;
}
Compiled with GCC-3.4.3 on a Linux box (Fedora Core 3 x86) the output
looks like this:

$ gcc -Wall -pedantic -std=c99 test.c -o test
$ ./test
10045.000000 = 10045
20045.000000 = 20044
20045.000000 = 20045

How could this be?

Thanks in advance!
- Pascal


What output did you expect? What do you expect from "%lf" format?

--
Joe Wright mailto:jo********@comcast.net
"Everything should be made as simple as possible, but not simpler."
--- Albert Einstein ---
Nov 14 '05 #8

P: n/a
Joe Wright <jo********@comcast.net> writes:
[snip]
What output did you expect? What do you expect from "%lf" format?


Given a C99 conforming printf, I'd expect "%lf" to be equivalent to
"%f", expecting a double argument. (In C90, it's undefined behavior,
but I don't think it's causing the symptoms the OP is seeing.)

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #9

P: n/a
In message <ln************@nuthaus.mib.org>
Keith Thompson <ks***@mib.org> wrote:
I think there's an issue on x86 systems with intermediate results
being stored in extended-precision registers. It looks like the
result of x*100 is being stored with extra precision when it's passed
directly to printf(), but not when it's the operand of a cast to int.
Or something like that.


Which is the reason I asserted the other day that x86 gcc is not C90
conformant - these problems keep coming up with its floating point support.

Someone lept to its defence and claimed that this wasn't true, and these
floating point problems only applied to some obsolete code generation
options, not when using MMX (or whatever it is). Perhaps a gcc person could
clarify how to change this option to get C90 conformance?

--
Kevin Bracey, Principal Software Engineer
Tematic Ltd Tel: +44 (0) 1223 503464
3 Signet Court, Swann Road, Fax: +44 (0) 1728 727430
Cambridge, CB5 8LA, United Kingdom WWW: http://www.tematic.com/
Nov 14 '05 #10

P: n/a
Kevin Bracey <ke**********@tematic.com> writes:
In message <ln************@nuthaus.mib.org>
Keith Thompson <ks***@mib.org> wrote:
I think there's an issue on x86 systems with intermediate results
being stored in extended-precision registers. It looks like the
result of x*100 is being stored with extra precision when it's passed
directly to printf(), but not when it's the operand of a cast to int.
Or something like that.
Which is the reason I asserted the other day that x86 gcc is not C90
conformant - these problems keep coming up with its floating point support.


Is this true even with -fno-fast-math? (That should be the default.)
Someone lept to its defence and claimed that this wasn't true, and these
floating point problems only applied to some obsolete code generation
options, not when using MMX (or whatever it is). Perhaps a gcc person could
clarify how to change this option to get C90 conformance?


This probably refers to -mfpmath=sse.

Followups set.
--
A competent C programmer knows how to write C programs correctly,
a C expert knows enough to argue with Dan Pop, and a C expert
expert knows not to bother.
Nov 14 '05 #11

P: n/a

Keith Thompson wrote:

On x86 systems, I get the following output:

x = 200.4499999999999886313162278383970260620117 --> 200
x * 100 = 20045.0000000000000000000000000000000000000000 --> 20044 x_times_100 = 20045.0000000000000000000000000000000000000000 --> 20045
On a SPARC system, I get:

x = 200.4499999999999886313162278383970260620117 --> 200
x * 100 = 20045.0000000000000000000000000000000000000000 --> 20045 x_times_100 = 20045.0000000000000000000000000000000000000000 --> 20045
There's no guarantee that 200.45 * 100 >= 20045.0 -- but if it is, the result of converting 20045.0 to int should be 20045, not 20044.

I think there's an issue on x86 systems with intermediate results
being stored in extended-precision registers. It looks like the
result of x*100 is being stored with extra precision when it's passed
directly to printf(), but not when it's the operand of a cast to int.
Or something like that.


There's more to it than that; gcc on any x86 BSD machine produces the
correct result.

Nov 14 '05 #12

P: n/a
In article <ln************@nuthaus.mib.org>, ks***@mib.org says...

Here's another program that exhibits the problem:

#include <stdio.h>

int main(void)
{
double x = 200.45;
double x_times_100 = x * 100;

printf("x = %46.40f --> %d\n", x, (int)x);
printf("x * 100 = %46.40f --> %d\n", x * 100, (int)(x * 100));
printf("x_times_100 = %46.40f --> %d\n", x_times_100, (int)x_times_100);

return 0;
}

On x86 systems, I get the following output:

x = 200.4499999999999886313162278383970260620117 --> 200
x * 100 = 20045.0000000000000000000000000000000000000000 --> 20044
x_times_100 = 20045.0000000000000000000000000000000000000000 --> 20045

On a SPARC system, I get:

x = 200.4499999999999886313162278383970260620117 --> 200
x * 100 = 20045.0000000000000000000000000000000000000000 --> 20045
x_times_100 = 20045.0000000000000000000000000000000000000000 --> 20045


I get exactly the same results using gcc 4.0 on Mac OS X 10.4.1 and
a G5, compiling for either 32-bit or 64-bit binary.

--
Randy Howard (2reply remove FOOBAR)
Life should not be a journey to the grave with the intention of arriving
safely in an attractive and well preserved body, but rather to skid in
sideways, chocolate in one hand, martini in the other, body thoroughly
used up, totally worn out and screaming "WOO HOO what a ride!!"

Nov 14 '05 #13

P: n/a
On Sun, 22 May 2005 08:43:05 +0000, Randy Howard wrote:
On x86 systems, I get the following output:

x = 200.4499999999999886313162278383970260620117 --> 200
x * 100 = 20045.0000000000000000000000000000000000000000 --> 20044
x_times_100 = 20045.0000000000000000000000000000000000000000 --> 20045

On a SPARC system, I get:

x = 200.4499999999999886313162278383970260620117 --> 200
x * 100 = 20045.0000000000000000000000000000000000000000 --> 20045
x_times_100 = 20045.0000000000000000000000000000000000000000 --> 20045


I get exactly the same results using gcc 4.0 on Mac OS X 10.4.1 and
a G5, compiling for either 32-bit or 64-bit binary.


On the other hand compiling with Microsoft's VS.NET C/C++ compiler
produces correct results. I tend to consider this a GCC related bug.

- Pascal

Nov 14 '05 #14

P: n/a
Pascal Gallois wrote:
On Sun, 22 May 2005 08:43:05 +0000, Randy Howard wrote:
On x86 systems, I get the following output:

x = 200.4499999999999886313162278383970260620117 --> 200
x * 100 = 20045.0000000000000000000000000000000000000000 --> 20044
x_times_100 = 20045.0000000000000000000000000000000000000000 --> 20045

On a SPARC system, I get:

x = 200.4499999999999886313162278383970260620117 --> 200
x * 100 = 20045.0000000000000000000000000000000000000000 --> 20045
x_times_100 = 20045.0000000000000000000000000000000000000000 --> 20045


I get exactly the same results using gcc 4.0 on Mac OS X 10.4.1 and
a G5, compiling for either 32-bit or 64-bit binary.


On the other hand compiling with Microsoft's VS.NET C/C++ compiler
produces correct results. I tend to consider this a GCC related bug.

You haven't yet revealed which options and rounding modes you use. I guess
you're saying that the defaults of gcc are buggy compared with the defaults
of an unspecified Microsoft compiler. gcc defaults were determined by
relatively open consensus among developers who want support for long
double, while your Microsoft compiler is unlikely to support long double.
You don't take any responsibility for setting equivalent options; for
example gcc -march=pentium4 -mfpmath=sse
or setting 53-bit precision mode when running the gcc x87 version, when that
is the default run-time setting for 32-bit MSVC.

--
Tim Prince
Nov 14 '05 #15

P: n/a
>In article <ln************@nuthaus.mib.org>, ks***@mib.org says...
On x86 systems, I get [result A] ...
On a SPARC system, I get [different result B].

In article <MP***********************@news.verizon.net>
Randy Howard <ra*********@FOOverizonBAR.net> wrote:I get exactly the same results using gcc 4.0 on Mac OS X 10.4.1 and
a G5, compiling for either 32-bit or 64-bit binary.


Exactly the same as Result A, or exactly the same as Result B?

(I do not really need to know; I merely note that your post was
ambiguous, so that the other follow-ups to it, which made claims
about gcc, are inconclusive at best.)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (4039.22'N, 11150.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 14 '05 #16

P: n/a
In article <d6********@news2.newsguy.com>, no****@torek.net says...
In article <ln************@nuthaus.mib.org>, ks***@mib.org says...
On x86 systems, I get [result A] ...
On a SPARC system, I get [different result B].


In article <MP***********************@news.verizon.net>
Randy Howard <ra*********@FOOverizonBAR.net> wrote:
I get exactly the same results using gcc 4.0 on Mac OS X 10.4.1 and
a G5, compiling for either 32-bit or 64-bit binary.


Exactly the same as Result A, or exactly the same as Result B?


I'm sorry, you're exactly right. Not sure why I wasn't more clear.
I get the same results that were reported earlier on the Sparc.

--
Randy Howard (2reply remove FOOBAR)
Life should not be a journey to the grave with the intention of arriving
safely in an attractive and well preserved body, but rather to skid in
sideways, chocolate in one hand, martini in the other, body thoroughly
used up, totally worn out and screaming "WOO HOO what a ride!!"

Nov 14 '05 #17

P: n/a
In article <pa****************************@nowhere.net> Pascal Gallois <pg@nowhere.net> writes:
On Sun, 22 May 2005 08:43:05 +0000, Randy Howard wrote:
On x86 systems, I get the following output:

x = 200.4499999999999886313162278383970260620117 --> 200
x * 100 = 20045.0000000000000000000000000000000000000000 --> 20044
x_times_100 = 20045.0000000000000000000000000000000000000000 --> 20045

On a SPARC system, I get:

x = 200.4499999999999886313162278383970260620117 --> 200
x * 100 = 20045.0000000000000000000000000000000000000000 --> 20045
x_times_100 = 20045.0000000000000000000000000000000000000000 --> 20045


I get exactly the same results using gcc 4.0 on Mac OS X 10.4.1 and
a G5, compiling for either 32-bit or 64-bit binary.


On the other hand compiling with Microsoft's VS.NET C/C++ compiler
produces correct results. I tend to consider this a GCC related bug.


What is the correct result? Assuming IEEE double, 200.45 is represented
as:
11001000.01110011001100110011001100110011001100110 0110
multiplying by 100 gives:
100111001001100.1111111111111111111111111111111111 11111011
and rounding that to double again yields:
100111001001101.0000000000000000000000000000000000 0000
or exactly 2045.

A SPARC has no extended precision, but an x86 has. The standard allows
for excess precision in expressions, and that is why int(x * 100) == 2044
on some systems but not on other. Note that when x * 100 is passed as
an argument to printf the excess precision is lost because the argument
is rounded to double.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Nov 14 '05 #18

P: n/a
On Sun, 22 May 2005 15:21:49 +0000, Tim Prince wrote:
You haven't yet revealed which options and rounding modes you use. I guess
you're saying that the defaults of gcc are buggy compared with the defaults
of an unspecified Microsoft compiler. gcc defaults were determined by
relatively open consensus among developers who want support for long
double, while your Microsoft compiler is unlikely to support long double.
You don't take any responsibility for setting equivalent options; for
example gcc -march=pentium4 -mfpmath=sse
or setting 53-bit precision mode when running the gcc x87 version, when that
is the default run-time setting for 32-bit MSVC.


My conclusions were premature and I apologize for that. I posted my
compiler switches in my original post (-Wall -pedantic -std=c99). The
suggested -fno-fast-math from Ben Pfaff did not change the result and my
response went to gnu.gcc.help since that was set as the followup. AFAIK it
was never suggested to use -march=pentium4 -mfpmath=sse. With those
two switches I indeed get the expected result of 20045.

I understand from Dik T. Winter's reply that excess precision in
expressions is platform dependent. However I find this specification
unfortunate since it produces different results for i and j (if -march !=
pentium4 for example) in:

double m = 200.45;
double n = m * 100;
int i = m * 100;
int j = n;

- Pascal

Nov 14 '05 #19

P: n/a
Pascal Gallois wrote:
The
suggested -fno-fast-math from Ben Pfaff did not change the result and my
response went to gnu.gcc.help since that was set as the followup. AFAIK it
was never suggested to use -march=pentium4 -mfpmath=sse. With those
two switches I indeed get the expected result of 20045.

I understand from Dik T. Winter's reply that excess precision in
expressions is platform dependent. However I find this specification
unfortunate since it produces different results for i and j (if -march !=
pentium4 for example) in:

double m = 200.45;
double n = m * 100;
int i = m * 100;
int j = n;

- Pascal

gcc never sets -ffast-math unless given explicitly on the command line. As
you found out, it should not have any effect in this example.
In the long run, however, you can easily cause yourself and your colleagues
a lot of grief by ignoring the distinction between decimal and binary
arithmetic.
--
Tim Prince
Nov 14 '05 #20

P: n/a
On Mon, 23 May 2005 12:22:59 +0000, Tim Prince wrote:
In the long run, however, you can easily cause yourself and your colleagues
a lot of grief by ignoring the distinction between decimal and binary
arithmetic.


This is more an issue with excess precision and certainly x86 related. And
it also has to do with GCC insofar as even if you cast the value of
an expression involving excess precision to double which should get rid of
excess bits, it still goes wrong (against the standard (6.3.1.8 and
footnote 52)).

A possible solution would also be to use long double: long double m =
200.45L; The trailing L causes proper initialization of extended
precision bits.

- Pascal

Nov 14 '05 #21

This discussion thread is closed

Replies have been disabled for this discussion.