Confused to printf %f

T

timmu

Someone asked me a question about integer division and printf
yesterday,
I tell him he should do a casting to float/double before you do any
interger division.
But he doesn't think so, so I try to do some example to explain,
However, after some trying,
I confused when I try to do some integer constant division,
I know I should do float division something like 3.0/6.0,
but I'm still confused where the -0.124709 comes for the following
example?
I just add some dummy variables, but the result will show some
different.
did I overflow anything?

The situation is somehow a little different for a different gcc
compiler, it's really wierd.


//example 1
#include <stdio.h>
int main()
{
//double i;
//long a=1,b=4;
printf("%f\n",3/7);
printf("%f\n",5/7);
printf("%.2f\n",5/7);
printf("%f\n",5.0/7.0);
printf("%d\n",4/7);
return 0;
}

-bash-3.00$ gcc z.c
-bash-3.00$ ./a.out
-0.124709
-0.124709
-0.12
0.714286
0

**********************

//example 2:
#include <stdio.h>
int main()
{
double i;
long a=1,b=4;
printf("%f\n",3/7);
printf("%f\n",5/7);
printf("%.2f\n",5/7);
printf("%f\n",5.0/7.0);
printf("%d\n",4/7);
return 0;
}
-bash-3.00$ gcc z.c
-bash-3.00$ ./a.out
0.000000
0.000000
0.00
0.714286
0

********
//example 3:
#include <stdio.h>
int main()
{
double i;
//long a=1,b=4;
printf("%f\n",3/7);
printf("%f\n",5/7);
printf("%.2f\n",5/7);
printf("%f\n",5.0/7.0);
printf("%d\n",4/7);
return 0;
}

-bash-3.00$ gcc -v
Using built-in specs.
Configured with: FreeBSD/i386 system compiler
Thread model: posix
gcc version 3.4.2 [FreeBSD] 20040728

-bash-3.00$ gcc z.c
-bash-3.00$ ./a.out
-0.124709
-0.124709
-0.12
0.714286
0
-bash-3.00$ gcc41 z.c
-bash-3.00$ ./a.out
0.000000
0.000000
0.00
0.714286
0

Thanks for any information!
 
R

Richard Heathfield

timmu said:
Someone asked me a question about integer division and printf
yesterday,
I tell him he should do a casting to float/double before you do any
interger division.
Why?

But he doesn't think so, so I try to do some example to explain,
However, after some trying,
I confused when I try to do some integer constant division,

If you are confused yourself, why did you "tell him" what he should do?
I know I should do float division something like 3.0/6.0,

That isn't a float division.
but I'm still confused where the -0.124709 comes for the following
example?

You lied to the compiler, and it got its revenge.

//example 1
#include <stdio.h>
int main()
{
//double i;
//long a=1,b=4;
printf("%f\n",3/7);

3 is an int, 7 is an int, and so it's not surprising that 3/7 is an int. But
by using %f, you said to printf: "I'm giving you a double". But you didn't
give it a double. You gave it an int. If you lie to the compiler, it will
wreak its revenge.

Use %d to print ints.

<snip>
 
T

timmu

Richard Heathfield 寫é“:
timmu said:
^^^^^^^^^
sorry I mean float division.
Why?


If you are confused yourself, why did you "tell him" what he should do?

I really means 3.0/7.0, isn't this double-float division?
That isn't a float division.


You lied to the compiler, and it got its revenge.



3 is an int, 7 is an int, and so it's not surprising that 3/7 is an int. But
by using %f, you said to printf: "I'm giving you a double". But you didn't
give it a double. You gave it an int. If you lie to the compiler, it will
wreak its revenge.

Use %d to print ints.

<snip>

what I really confused is what compiler doing here.
anyway thx for your information.
 
R

Richard Heathfield

timmu said:
Richard Heathfield [said]?
timmu said:
^^^^^^^^^
sorry I mean float division.

Same question. Why cast?

I really means 3.0/7.0, isn't this double-float division?

It's a division of a double by a double. The result is a double.
what I really confused is what compiler doing here.

The compiler merely generates a call to printf. It is printf itself which
attempts to evaluate the data you pass to it. If you give it the wrong kind
of data, you shouldn't be surprised if you get the wrong answer.
 
R

Robbie Hatley

timmu said:
printf("%f\n",3/7); // ERROR use %d to print ints
printf("%f\n",5.0/7.0); // OK (prints "0.714286")
printf("%d\n",4/7); // OK (prints "0")

I hope you are aware that in C, the following fractions are ALL
equal to zero?

1/7 2/7 3/7 4/7 5/7 6/7

That's because with integer division, the fractional part is
discarded. If you want to keep the fractional part, you need
to write:

1.0/7.0 2.0/7.0 3.0/7.0 4.0/7.0 5.0/7.0 6.0/7.0

and also use %f instead of %d in your printf() call.

--
Cheers,
Robbie Hatley
East Tustin, CA, USA
lone wolf intj at pac bell dot net
(put "[usenet]" in subject to bypass spam filter)
home dot pac bell dot net slant earnur slant
 
K

Keith Thompson

timmu said:
Someone asked me a question about integer division and printf
yesterday,
I tell him he should do a casting to float/double before you do any
interger division.

It depends on what you're trying to do.

The division operator can be applied to either integer or
floating-point operands. ("Integer" refers any integer type: int,
short, long, long long, unsigned char, etc.; "floating-point" refers
to any of float, double, long double.)

If you want to divide integers, divide integers. If you want to
divide floating-point values, do so. There's no general rule that
says you should use one or the other.

In the usual mathematical sense, 3/5 is 0.6; to do that in C, you need
to use floating-point division: 3.0/5.0 (or, for variables rather than
constants, something like (double)x/(double)y). But in C, 3/5 is an
integer division, yielding the value 0 (use the "%" operation if you
want the remainder).

[...]
//example 1
#include <stdio.h>
int main()
{
//double i;
//long a=1,b=4;
printf("%f\n",3/7);
printf("%f\n",5/7);
printf("%.2f\n",5/7);

3/7 and 5/7 are expressions of type int; you need to use "%d" to print
them. Don't try to figure out the results of doing it wrong, just do
it right.

[snip]
 
M

Michael Mair

Keith said:
It depends on what you're trying to do.

The division operator can be applied to either integer or
floating-point operands. ("Integer" refers any integer type: int,
short, long, long long, unsigned char, etc.; "floating-point" refers
to any of float, double, long double.)

If you want to divide integers, divide integers. If you want to
divide floating-point values, do so. There's no general rule that
says you should use one or the other.

In the usual mathematical sense, 3/5 is 0.6; to do that in C, you need
to use floating-point division: 3.0/5.0 (or, for variables rather than
constants, something like (double)x/(double)y). But in C, 3/5 is an
integer division, yielding the value 0 (use the "%" operation if you
want the remainder).

As an aside: Some compilers' extensions ignore C semantics in
the following situation:
double foo = 3/5;
then gives you approximately 0.6.
I do no longer have the data about these things but I remember
some gcc 3.x having that "feature" when not invoked with -std=c89,
-ansi, or -std=c99 (more recent gcc versions behave correctly in
this respect) -- a very nasty surprise for a real programme
relying on "foo" having value 0.0 in this case (this came in via
macro) and also little bit embarrassing when trying to teach
students the difference between
3/5
and
3.0/5, 3/5.0, 3.0/5.0


<snip>

Cheers
Michael
 
K

Keith Thompson

Michael Mair said:
As an aside: Some compilers' extensions ignore C semantics in
the following situation:
double foo = 3/5;
then gives you approximately 0.6.
I do no longer have the data about these things but I remember
some gcc 3.x having that "feature" when not invoked with -std=c89,
-ansi, or -std=c99 (more recent gcc versions behave correctly in
this respect) -- a very nasty surprise for a real programme
relying on "foo" having value 0.0 in this case (this came in via
macro) and also little bit embarrassing when trying to teach
students the difference between
3/5
and
3.0/5, 3/5.0, 3.0/5.0

Hmm. I've just tried it with gcc versions 2.7.2.2, 2.8.1, 2.95.2,
3.0.4, 3.4.4, and 4.1.1; with each of them, it set foo to 0.0.

Program:

#include <stdio.h>
int main(void)
{
double foo = 3/5;
printf("foo = %g\n", foo);
return 0;
}

Command line:

gcc tmp.c -o tmp && ./tmp
 
M

Michael Mair

Keith said:
Hmm. I've just tried it with gcc versions 2.7.2.2, 2.8.1, 2.95.2,
3.0.4, 3.4.4, and 4.1.1; with each of them, it set foo to 0.0.

Program:

#include <stdio.h>
int main(void)
{
double foo = 3/5;
printf("foo = %g\n", foo);
return 0;
}

Command line:

gcc tmp.c -o tmp && ./tmp

Maybe it was a "feature" of only one version. I do no longer
have the programmes the students wrote nor do I know the version
of gcc this course went by but I remember the integer division
problem distinctly because I _was_ rather embarrassed at first
before figuring it out.
I found the exercise the translation of which essentially is to
to write a programme reading two ints m and M and do certain things
based on whether m > M, M - m < 4, M - m < 8, or else.
Among these things was calculating m/M and (m+M-1)/2.5 for the last
two cases and outputting them -- and, between the lines, remarking
on the difference between integer division and floating point
division... Unfortunately, m/M did not give 0 as expected.

-Michael
 
T

timmu

Robbie Hatley 寫é“:
I hope you are aware that in C, the following fractions are ALL
equal to zero?

1/7 2/7 3/7 4/7 5/7 6/7

That's because with integer division, the fractional part is
discarded. If you want to keep the fractional part, you need
to write:

1.0/7.0 2.0/7.0 3.0/7.0 4.0/7.0 5.0/7.0 6.0/7.0

and also use %f instead of %d in your printf() call.

thx for your information,

Of course I know interger divide interger will be integer,
I know double divide double will be double...
I know I should use %f instead of %d if I want to keep the fractional
part...
I knew it when my first year of C programming......

But my friend don't know, I try to explain it to him.
That's why I write the example in origin post.
I wrote the wrong statement because I want to show him "it's wrong!"
Of course I know I should use %f instead of %d...

I just don't satisfied the "you cheat compiler, so it revenge" answer,
I want to know the scene behind these, I want to know why the compiler
generate a different answer
for just adding some dummy variables.

thx informations anyway.
--
Cheers,
Robbie Hatley
East Tustin, CA, USA
lone wolf intj at pac bell dot net
(put "[usenet]" in subject to bypass spam filter)
home dot pac bell dot net slant earnur slant
 
K

Keith Thompson

timmu said:
Robbie Hatley 寫é“:

thx for your information,

Of course I know interger divide interger will be integer,
I know double divide double will be double...
I know I should use %f instead of %d if I want to keep the fractional
part...
I knew it when my first year of C programming......

But my friend don't know, I try to explain it to him.
That's why I write the example in origin post.
I wrote the wrong statement because I want to show him "it's wrong!"
Of course I know I should use %f instead of %d...

Great, I'm glad you know this. But that wasn't very clear from your
orginal article. And frankly, it doesn't matter much to us whether
it's you or your friend who didn't now this; we've never met either of
you. We can only answer what you ask.
I just don't satisfied the "you cheat compiler, so it revenge" answer,
I want to know the scene behind these, I want to know why the compiler
generate a different answer
for just adding some dummy variables.

The type of any expression in C (that includes subexpressions) is
almost always determined by the expression itself, not by the context
in which it appears.

In your example:

printf("%f\n", 3/7);

you use "%f" to promise printf that you're going to pass a double, but
then you pass an int.

Why does it print garbage rather than fixing you the error for you?
Because the standard says it's undefined behavior. C99 7.19.6.1p9 says:

If a conversion specification is invalid, the behavior is
undefined. If any argument is not the correct type for the
corresponding conversion specification, the behavior is undefined.

The phrase "the behavior is undefined" means that there are absolutely
no requirements on what the implementation has to do. It can behave
as you might expect, it can produce garbage, it can crash your
program, it can crash your computer, it can reformat your hard drive,
or, as the joke goes, it can make demons fly out of your nose.

Why does your particular implementation behave in some specific way?
I don't know, I don't really care, and you shouldn't either.

The implementation isn't literally getting revenge, it's just behaving
on the *assumption* that you didn't lie to it. Given a "%f" format,
printf will most likely try to print a floating-point value that it
fetches from the location where it would find one if you had passed
one. Since you didn't, it could use the bitwise representation of
your integer value and pretend it's of type double, or it could grab
some piece of garbage from somewhere. If the garbage doesn't
represent a valid value of type double, Bad Things Can Happen. But
different implementations of printf will do different things, and the
details are off-topic here.

Just don't do that.
 
T

timmu

Keith Thompson 寫é“:

Why does it print garbage rather than fixing you the error for you?
Because the standard says it's undefined behavior. C99 7.19.6.1p9 says:

If a conversion specification is invalid, the behavior is
undefined. If any argument is not the correct type for the
corresponding conversion specification, the behavior is undefined.

The phrase "the behavior is undefined" means that there are absolutely
no requirements on what the implementation has to do. It can behave
as you might expect, it can produce garbage, it can crash your
program, it can crash your computer, it can reformat your hard drive,
or, as the joke goes, it can make demons fly out of your nose.

Why does your particular implementation behave in some specific way?
I don't know, I don't really care, and you shouldn't either.

The implementation isn't literally getting revenge, it's just behaving
on the *assumption* that you didn't lie to it. Given a "%f" format,
printf will most likely try to print a floating-point value that it
fetches from the location where it would find one if you had passed
one. Since you didn't, it could use the bitwise representation of
your integer value and pretend it's of type double, or it could grab
some piece of garbage from somewhere. If the garbage doesn't
represent a valid value of type double, Bad Things Can Happen. But
different implementations of printf will do different things, and the
details are off-topic here.

Just don't do that.

thx, now I know the problem is because of different implementations to
a undefine behavior,
really thx for your well explanation.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top