-1

Must we always use %f to printf a %d value, and %lf to take input by scanf ? Why format specifier %d or %i does not work for a double variable but %f works and print a float output of an integer variable? Is it always safe to declare double and take input scanf with %lf and printf as %f?

My codes:

int main(void)
{

    double dnum = 7777;
    printf(" %f \n", dnum);

        return 0;
}

output is 7777.000000

int main(void)
{

    double dnum = 7777;
    printf(" %i \n", dnum);

        return 0;
}

Output is 0

int main(void)
{

    double dnum = 7777;
    printf(" %d \n", dnum);

        return 0;
}

Output is 0

phuclv
  • 37,963
  • 15
  • 156
  • 475
shyed2001
  • 35
  • 6
  • 3
    I'm not sure what you mean, "why"? They are defined to expect arguments of different types. – Nate Eldredge Jul 08 '20 at 14:07
  • 1
    When you define the variable `dnum` as a `double` it will never contain an integer value. No matter how you initialize it, the value of that `double` variable will always be a `double` precision floating point value. – Some programmer dude Jul 08 '20 at 14:08
  • 1
    The explanation for any particular behavior will depend on your specific platform. For instance, on some machines, floating point values are passed in one set of registers, and integers are passed in a different set. In that case your `printf(" %i \n", dnum);` may result in the number 7777.0 being loaded into a floating-point register, but `printf` prints the value from an integer register which contains something totally unrelated. – Nate Eldredge Jul 08 '20 at 14:09
  • 1
    The format specifier `%f` in function `printf` is not the format specifier for `float` (there isn't one) but for `double`. A `float` argument passed to variadic function gets promoted to `double` (not to be confused with the way `scanf` works). So your first example is *supposed* to work, and it does. You are passing a `double` value for `%f`. – Weather Vane Jul 08 '20 at 14:46
  • Does this answer your question? [What happens when I use the wrong format specifier?](https://stackoverflow.com/questions/16864552/what-happens-when-i-use-the-wrong-format-specifier) – phuclv Apr 11 '21 at 00:20

5 Answers5

1

Format and data mismatch in printf() invokes undefine behavior.

In your code, the type of dnum is double regardless of its actual value, which may be an integer.

%f can be used to print double, but neithor %d nor %i cannot be used.

MikeCAT
  • 73,922
  • 11
  • 45
  • 70
1

If you want to print double, you should use %g, %f or %e depending on the format that you want it to be printed.

%d is the same as %i (for printf) and it goes along with signed integer.

Jona
  • 1,218
  • 1
  • 10
  • 20
1

Welcome in the world of undefined behavior!

To use the %d conversion specifier to print a double value or the %f conversion specifier to print an int value invokes undefined behavior.

%f is furthermore meant to print a value of type double, not float. A passed float gets automatically promoted.

The C standard states:

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

Source: C18, 7.21.6.1/9


d,i - The int argument is converted to signed decimal in the style[-]dddd. The precision specifies the minimum number of digits to appear; if the value being converted can be represented in fewer digits, it is expanded with leading zeros. The default precision is 1. The result of converting a zero value with a precision of zero is no characters.

......

f,F - A double argument representing a floating-point number is converted to decimal notation in the style[-]ddd.ddd, where the number of digits after the decimal-point character is equal to the precision specification. If the precision is missing, it is taken as 6; if the precision is zero and the flag is not specified, no decimal-point character appears. If a decimal-point character appears, at least one digit appears before it. The value is rounded to the appropriate number of digits.

A double argument representing an infinity is converted in one of the styles[-]infor[-]infinity— which style is implementation-defined. A double argument representing a NaN is converted in one of the styles[-]nanor[-]nan(n-char-sequence)— which style, and the meaning of any n-char-sequence, is implementation-defined. The F conversion specifier produces INF,INFINITY, or NAN instead of inf, infinity, or nan, respectively.283)

Source: C18, §7.21.6.1/8


Summary:

Except for the first example, The output you get is any arbitrary value (unless the implementation didn't specified what happens else). It doesn't "work" in the one way nor in the other.

1

The format specifier: %d or %i expects the argument of signed int, if anything else is given in the formatted printf() statement, such as:

float f = 1.50235F;
printf("%d", f);

Will expect for signed int, if you pass a float or a double instead, it'll tend to undefined behavior and probably print 0.

In a more practical sense, if you want to do-it-yourself, you may add -Wformat flag and run the command in your command prompt or any command shell:

$ gcc -o main main.cpp -Wformat

Then you'll get a warning generated by the compiler similar to the following:

main.c: In function 'int main()':
main.c:9:14: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat=]
    9 |     printf("%d\n", d);
      |             ~^     ~
      |              |     |
      |              int   double
      |             %f

The double has the twice precision than a float could hold (i.e. double has 15 decimal digits of precision, while float has only 7. So, the %f type specifier could be used for double in this case too.)

To know further about the format specifiers: List of all format specifiers in C.

Rohan Bari
  • 7,482
  • 3
  • 14
  • 34
1

All conversion specifiers expect their corresponding argument to be a specific type; you can't arbitrarily mix and match them. %d and %i expect their corresponding argument to have type int - if it doesn't, the behavior is undefined and you'll (usually) get weird output. Integers and floating point values have very different binary representations and may have different sizes - most modern platforms use 32 bits to store integer values and 64 bits to store doubles, which affects how those values are interpreted in the printf code.

For an authoritative list of conversion specifiers and the types of arguments they take, refer to the C 2011 Online Draft, section 7.21.6.1 (The fprintf function).

John Bode
  • 119,563
  • 19
  • 122
  • 198