Why does this output of the same expression from printf differ from cout?
Asked Answered
E

9

26

I'm using Visual C++ 2012 and compiling from the command line the following files:

#include <stdio.h>
int main()
{
    printf("%.5f", 18/4+18%4);
    return 0;
} 

Linking with MSVCRT.LIB rather than LIBCMT to avoid runtime error R6002.
The value that is output is 0.00000 for this program.

However, if I perform the exact same thing in C++

 #include <iostream>
 using namespace std;
 int main()
 {
      cout << 18/4+18%4 << endl;
      return 0;
 }

Now, it prints out 6, like it should.

What's the difference? Is it to do with the languages themselves (C vs C++) or the output methods (cout vs printf), or is it just a quirk with MSVC?

Eskimo answered 30/9, 2013 at 20:3 Comment(10)
Try with "%d" instead of "%.5f".Foxhole
Seems as if your first code example printf format expects a float, but the arithmetic is integer arithmetic.Iridissa
your first example is purely integer arithmetic, the compiler properly compiled your codeReich
Also see stackoverflow.com/questions/8559198.Cottonwood
By the way, they didn't remove printf from C++ so you could have tried it there and seen immediately where the problem was.Briscoe
a nice demonstration of why strongly typed cout is better than loosy goosy printfWylie
Also note as I stated in my answer this use of printf invokes undefined behavior.Lyris
@Wylie I would have interpreted it as strongly typed printf format strings catching an error and cout silently making it work! :)Barter
@RobertHarvey Actually %f is double, in variadic functions floats are promoted to double.Lyris
@Barter - the printf code compiled and did the wrong thing , not what the dev intended and in fact invoked undefined behavior. cout did exactly what the dev expectedWylie
B
64

The expression 18/4+18%4 evaluates to an int, and you are requesting a float. You should always compile with warnings enabled, and pay attention to them (they say a warning is a bug waiting to happen, and they are right).

This is what my compiler (GCC 4.8.1) tells me (and even without enforcing -Wall):

warning: format ‘%.5f’ expects type ‘double’, but argument 2 has type ‘int’

On the other hand, the std::cout<< operation is able to deduce the type of your expression and correctly stream it to your screen.

Bootee answered 30/9, 2013 at 20:6 Comment(3)
The type of 18/4+18%4 is int, not "integer".Coquito
The %.5f format expects a double, not a float. (float arguments to printf are promoted to double but that's not relevant in this case.)Coquito
Tiny correction - don't pay attention to warnings. Use -Werror so they'll stop your compilation.Isolationism
K
37

The C function is being passed an integer, but you are telling it (with %f) to expect a double-precision floating point number, so it fails. The C++ function knows that it is being passed an integer, so it works properly.

Kazan answered 30/9, 2013 at 20:5 Comment(4)
The printf didn't fail (go kaboom). It just didn't work as expected.Dang
@DavidHammen: It certainly failed to work properly, but just because it prints 0 for him doesn't mean it will print 0 for everybody. For some people, it could go kaboom.Kazan
C11dr 7.21.6.1 9 "If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined."Daciadacie
@chux: Indeed, "kaboom" is actually one of the allowable outcomes! :)Kazan
L
12

In the C example this expression 18/4+18%4 will evaluate to an int since all the operands are integer constants but you are specifying that it is a double to printf and therefore it is will be processed incorrectly. On the other hand if you had used a Floating constant in the division part of the expression for example 18.0/4+18%4 the whole expression would have evaluated to a double. Alternatively you could have used "%d" in the format specifier as well.

This is also undefined behavior to incorrectly specify the format to printf and this also demonstrates why building with warnings is important, using gcc -Wall I receive the following warning(see it live):

warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ 

In C++ std::cout's operator<< has an overload for int and therefore that will be called in this case. We can see this overload an many others are required by the C++ draft standard, in section 27.7.3.1 Class template basic_ostream we find the following operator declaration:

basic_ostream<charT,traits>& operator<<(int n);

For completeness sake, circling back to the undefined behavior, the C99 draft standard in section 7.19.6.1 The fprintf function which printf's section refers back to for the format string paragraph 9 says:

If a conversion specification is invalid, the behavior is undefined.[...]

Lyris answered 30/9, 2013 at 20:7 Comment(1)
Apparently gcc and clang warn for this regardless of warning settings, which is good but this does not change the fact that having warning enabled will prevent many bugs.Lyris
R
11

The expression

18 / 4 + 18 % 4

evaluates to an int.

But the printf format string "%.5f" expects a double.

With c++ and ostreams, the language can determine the output type automatically.

Just change your C-code to following:

#include <stdio.h>
int main()
{
    printf("%d", 18 / 4 + 18 % 4);
    return 0;
}
Responsory answered 30/9, 2013 at 20:8 Comment(0)
W
8

Others have correctly pointed out the int/double mismatch in your printf() statement. I just want to comment on your statement, "Linking with MSVCRT.LIB rather than LIBCMT to avoid runtime error R6002." A program this simple should not unduly tax the runtime environment, so a runtime error like this should be a red flag of undefined behavior in your code.

A quick Google of "MSVCRT R6002" says:

C Run-Time Error R6002 floating-point support not loaded

The necessary floating-point library was not linked. To fix by checking the following possible causes

  1. The program was compiled or linked with an option, such as /FPi87, that requires a coprocessor, but the program was run on a machine that did not have a coprocessor installed.
  2. A format string for a printf_s or scanf_s function contained a floating-point format specification and the program did not contain any floating-point values or variables.
  3. The compiler minimizes a program's size by loading floating-point support only when necessary. The compiler cannot detect floating-point format specifications in format strings, so it does not load the necessary floating-point routines.
  4. Use a floating-point argument to correspond to the floating-point format specification, or perform a floating-point assignment elsewhere in the program. This causes floating-point support to be loaded.
  5. In a mixed-language program, a C library was specified before a FORTRAN library when the program was linked. Relink and specify the C library last.

The lesson here, of course, is that you should pay close attention to compiler and runtime warnings & errors. When in doubt, always assume the problem is in your code, not in the compiler's.

Willner answered 1/10, 2013 at 11:3 Comment(1)
I just figured linking to a library that had floating point support built in (MSVCRT does) then it'd solve the problem. I understand the mistake now, though.Eskimo
S
4

In C because you explicitly specify floating point ("%f") in your printf format specifier, so it's expecting a floating point argument. But you're giving it an "int" argument, hence the problem.

Depending on what you're trying to do, you can:

1) Casting your (otherwise integer) expression to float

and/or

2) Using setprecision in your cout stream, just as you'd use "%.5f" in C:

#include <iostream>
using namespace std;
int main()
{
   float x = 18/4+18%4;
   std::cout << std::setprecision(5) << x << endl;
   return 0;
}

3) If you want integer, use printf ("%d", 18/4+18%4);

Stunk answered 30/9, 2013 at 20:5 Comment(0)
O
2

If you want the same behaviour (and response) you'd better to code

printf("%d\n", 18/4 + 18%4);

to get an int response instead of a floating point one. You'll get the same result as << operator selected is

ostream& std::operator<<(ostream&, const int);

Otherwise, you can use explicitly

printf("%.5f\n", (double)(18/4 + 18%4));

to get 6.00000 result.

Offprint answered 30/9, 2013 at 21:50 Comment(0)
R
1

Have one of your numbers be a floating point value:

#include <stdio.h>
int main()
{

    printf("%.0f", 18/4.0+18%4);
    return 0;
} 
Raker answered 30/9, 2013 at 20:16 Comment(0)
M
0

One possible alternative is, typecasting the literals (or the expression itself):

#include <stdio.h>

int main(void)
{
    printf("%.5f", (float)18/4+18%4);
    return 0;
}
Marsden answered 1/10, 2013 at 10:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.