conversions from float to int?

C

chandanlinster

I came to know that using format specifiers(in printf or scanf) to
convert between float and int is a really bad idea. This is because the
printf prints junk values when it encounters such a type of conversion.

For eg:

#include <stdio.h>

int
main()
{
float a = 3.1415;

printf("a = %d\n", a);

return 0;
}

/*Output*/
a = -1073741824

My doubt is, Would this case only apply between INTs and FLOATs, or is
the same reasoning valid for other types.

If the same reasoning is valid for other types, how come the following
program works correctly?

#include <stdio.h>

int
main()
{
unsigned int ch;
for(ch = 65;ch <= 255;ch++)
printf("%d %c\n", ch, ch++);
return 0;
}
 
K

Keith Thompson

chandanlinster said:
I came to know that using format specifiers(in printf or scanf) to
convert between float and int is a really bad idea. This is because the
printf prints junk values when it encounters such a type of conversion.

Right, because there's no conversion at all.
For eg:

#include <stdio.h>

int
main()
{
float a = 3.1415;

printf("a = %d\n", a);

return 0;
}

/*Output*/
a = -1073741824

The "%d" format promises the printf() function that the corresponding
argument is going to be of type int. You pass it a float instead
(actually a double; see below). You lie to printf(), and it believes
you and gives you garbage.
My doubt is, Would this case only apply between INTs and FLOATs, or is
the same reasoning valid for other types.

It applies in general. The format has to match the actual type of the
corresponding argument after promotion.
If the same reasoning is valid for other types, how come the following
program works correctly?

#include <stdio.h>

int
main()
{
unsigned int ch;
for(ch = 65;ch <= 255;ch++)
printf("%d %c\n", ch, ch++);
return 0;
}

Because integer types smaller than int, and floating-point types
smaller than double, are promoted (implicitly converted) when passed
as arguments where the compiler doesn't know the expected type.

In the printf call, the argument ch is an unsigned int, so you
*should* use "%u" -- but the standard guarantees that, for values
representable as both int and unsigned int, both types use the same
representation. It's not a guarantee you should take advantage of.

The argument ch++ is also of type unsigned int. The "%c" format
actually tells printf to expect an int, because that's what characters
are generally promoted to. Again, you give it an unsigned int where
it expects an int, and again it happens to work.
 
P

Peter Nilsson

Keith said:
Right, because there's no conversion at all.


The "%d" format promises the printf() function that the corresponding
argument is going to be of type int. You pass it a float instead
(actually a double; see below). You lie to printf(), and it believes
you and gives you garbage.


It applies in general. The format has to match the actual type of the
corresponding argument after promotion.

This invokes undefined behaviour, but I think Keith presumes you were
really talking about...

printf("%d %c\n", ch, ch);
Because integer types smaller than int,

More precisely, integer types with a rank lower than int.
and floating-point types smaller than double,

More precisely, float.
are promoted (implicitly converted) when passed
as arguments where the compiler doesn't know the expected type.

In the printf call, the argument ch is an unsigned int, so you
*should* use "%u"

Since the value is in range, that is debatable.
-- but the standard guarantees that, for values
representable as both int and unsigned int, both types use the same
representation.

True, but it's relevance may not be obvious to the casual observer. ;-)
It's not a guarantee you should take advantage of.

The argument ch++ is also of type unsigned int. The "%c" format
actually tells printf to expect an int, because that's what characters
are generally promoted to.

This is a separate issue. The printf description itself states that the
wrong (promoted) argument type will result in undefined behaviour.

However, note that va_arg(ap, int) is _supposed_ to retrieve 42 if the
corresponding argument was 42u. Although the standard isn't _explicit_,
it is a reasonable interpretation that printf (et al) implicitly use
the same
variable arguments mechanism as defined in 7.15, <stdarg.h>.

So, some people think the following is valid, others don't...

printf("%d\n", 42u);
 
K

Keith Thompson

Peter Nilsson said:
This invokes undefined behaviour, but I think Keith presumes you were
really talking about...

printf("%d %c\n", ch, ch);

No, I just overlooked the undefined behavior (caused by the occurrence
of ch and ch++ in the same expression with no intervening sequence
point).

[...]
Since the value is in range, that is debatable.

Oh? I can't think of any reason to use "%d" rather than "%u" for an
unsigned argument. If it's guaranteed to be in range, they should
both work, but "%u" is clearer. If it's not guaranteed to be in
range, "%u" is correct and "%d" is incorrect. Where's the debate?
 

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

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top