Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens to a float variable when %d is used in a printf?

I'm trying to learn C by reading C Programming Language, 2nd Edition . I have some programming experience but not with C.

I'm currently in Chapter 1. I have the following code:


  float f;
  for (f = 0.0; f <= 3; f += 1.1)
      printf("A: %3f B: %6.2f\n", f, f + 0.15);

It prints the output:

A: 0.000000 B:   0.15
A: 1.100000 B:   1.25
A: 2.200000 B:   2.35

Looks fine.


Now I change the printf as follows:

printf("A: %3d B: %6.2f\n", f, f + 0.15);

The new output is

A:   0 B:   0.00
A: -1610612736 B:   0.00
A: -1610612736 B: -625777476808257557292155887552002761191109083510753486844893290688350183831589633800863219712.00

What's going on here? I would expect the float to be converted to int because I used %d but that's not what happened. Also, why did the the value B go wrong as well? What happened to f here?

like image 780
Gollum Avatar asked Sep 20 '11 04:09

Gollum


People also ask

Can I use %d for float?

No. The d stands for “decimal”, and a %d format requires an argument of type int . There are several formats for type double : %f , %e , %g . You can use %lf , but the l is quietly ignored (this was added in the 1999 standard for symmetry with scanf , which has to distinguish between float and double ).

Can we use %d for float in c?

Let's see an example of taking and displaying a float , a double and a char value. So, you can see here that %d is used for integers, %f for floats and %c for characters.

What is %d %s in c language?

%s refers to a string %d refers to an integer %c refers to a character. Therefore: %s%d%s%c\n prints the string "The first character in sting ", %d prints i, %s prints " is ", and %c prints str[0].

Is %d for double?

%d stands for decimal and it expects an argument of type int (or some smaller signed integer type that then gets promoted). Floating-point types float and double both get passed the same way (promoted to double ) and both of them use %f .


2 Answers

When you called:

printf("A: %3d B: %6.2f\n", f, f + 0.15);

C automatically converts the float values to double (it is a standard conversion made when you call a function that takes variable arguments, such as int printf(const char *fmt, ...);). For sake of argument, we will assume that sizeof(int) is 4 and sizeof(double) is 8 (there are exceptions, but they are few and far between).

The call, therefore, has pushed a pointer onto the stack, plus an 8-byte double for f, and another 8-byte double for f + 0.15. When it is processing the format string, the %d tells printf() that you pushed a 4-byte int onto the stack after the format string. Since that is not what you did, you have invoked undefined behaviour; whatever happens next is OK according to the C standard.

However, the most likely implementation blithely reads 4 bytes and prints them as if they were an int (it trusts you to tell it the truth). Then it comes across the %6.2f format; it will read 8-bytes off the stack as a double. There's an outside chance that this would cause a memory fault for misaligned access (it would require a 64-bit machine with a requirement that double be aligned on an 8-byte boundary, such as a SPARC), or it will read 4 bytes from f and 4 bytes from f + 0.15, putting them together to create some rather unexpected double value -- as your example shows.

like image 172
Jonathan Leffler Avatar answered Oct 06 '22 19:10

Jonathan Leffler


Printf will treat the memory you point as however you tell it to. There is no conversion going on. It is treating the memory that represents the float as an int. Because the two are stored differently, you get what is essentially a random number.

If you want to output your float as an integer, you should cast it first:

printf("A: %3d B: %6.2f\n", (int)f, f + 0.15); 
like image 43
Steve Rowe Avatar answered Oct 06 '22 19:10

Steve Rowe