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?

Peopleware
  • 1,399
  • 1
  • 25
  • 42
Govind Parmar
  • 20,656
  • 7
  • 53
  • 85

9 Answers9

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.

Escualo
  • 40,844
  • 23
  • 87
  • 135
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.

Gabe
  • 84,912
  • 12
  • 139
  • 238
  • 1
    The `printf` didn't fail (go kaboom). It just didn't work as expected. – David Hammen Sep 30 '13 at 20:43
  • 2
    @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. – Gabe Sep 30 '13 at 21:06
  • 11
    C11dr 7.21.6.1 9 "If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined." – chux - Reinstate Monica Sep 30 '13 at 21:33
  • 4
    @chux: Indeed, "kaboom" is actually one of the allowable outcomes! :) – Gabe Sep 30 '13 at 23:08
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.[...]

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 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. – Shafik Yaghmour Oct 01 '13 at 01:25
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;
}
villekulla
  • 1,039
  • 5
  • 15
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.

Drew Hall
  • 28,429
  • 12
  • 61
  • 81
  • 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. – Govind Parmar Oct 01 '13 at 16:24
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);

paulsm4
  • 114,292
  • 17
  • 138
  • 190
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.

Sunny R Gupta
  • 5,026
  • 1
  • 31
  • 40
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;
} 
smac89
  • 39,374
  • 15
  • 132
  • 179
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;
}
Dhruv Saxena
  • 1,336
  • 2
  • 12
  • 29