¿Por qué esta salida de la misma expresión de printf difiere de cout?


Estoy usando Visual C++ 2012 y compilando desde la línea de comandos los siguientes archivos:

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

Enlace con MSVCRT.LIB en lugar de LIBCMT para evitar el error de tiempo de ejecución R6002.
El valor de salida es 0.00000 para este programa.

Sin embargo, si realizo exactamente lo mismo en C++

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

Ahora, imprime 6, como debería.

¿Cuál es la diferencia? ¿Tiene que ver con los propios lenguajes (C vs C++) o los métodos de salida (cout vs printf), o ¿solo una peculiaridad con MSVC?

Author: Peopleware, 2013-10-01

9 answers

La expresión 18/4+18%4 se evalúa como un int, y usted está solicitando un flotador. Siempre debe compilar con las advertencias habilitadas, y prestar atención a ellas (dicen que una advertencia es un error que está a punto de ocurrir, y tienen razón).

Esto es lo que mi compilador (GCC 4.8.1) me dice (e incluso sin hacer cumplir -Wall):

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

Por otro lado, la operación std::cout<< es capaz de deducir el tipo de su expresión y transmitirla correctamente a su pantalla.

 64
Author: Escualo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-30 21:08:36

A la función C se le pasa un entero, pero le estás diciendo (con %f) que espere un número de coma flotante de doble precisión, por lo que falla. La función C++ sabe que se le está pasando un entero, por lo que funciona correctamente.

 37
Author: Gabe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-30 23:09:56

En el ejemplo C esta expresión 18/4+18%4 evaluará a un int ya que todos los operandos son constantes enteras pero usted está especificando que es un doble a printf y por lo tanto se procesará incorrectamente. Por otro lado, si hubiera utilizado una constante flotante en la parte de división de la expresión, por ejemplo 18.0/4+18%4, toda la expresión se habría evaluado a una doble. Alternativamente, podría haber utilizado "%d" en el especificador de formato también.

Esto también es comportamiento indefinido para especificar incorrectamente el formato a printf y esto también demuestra por qué construir con advertencias es importante, usando gcc -Wall recibo la siguiente advertencia (véalo en vivo):

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

In C++ std:: cout's operador tiene una sobrecarga para int y por lo tanto que se llamará en este caso. Podemos ver esta sobrecarga y muchos otros son requeridos por el C++ draft standard, en la sección 27.7.3.1 Plantilla de clase basic_ostream encontramos la siguiente declaración de operador:

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

Para completar, volviendo al comportamiento indefinido, el C99 draft standard en la sección 7.19.6.1 La función fprintf a la que se refiere la sección printf para el párrafo de cadena de formato 9 dice:

Si una especificación de conversión no es válida, el comportamiento es indefinido.[...]

 12
Author: Shafik Yaghmour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-01 01:04:54

La expresión

18 / 4 + 18 % 4

Se evalúa como un int.

Pero la cadena de formato printf "%.5f " espera un doble.

Con c++ y ostreams, el lenguaje puede determinar el tipo de salida automáticamente.

Simplemente cambie su código C a lo siguiente:

#include <stdio.h>
int main()
{
    printf("%d", 18 / 4 + 18 % 4);
    return 0;
}
 11
Author: villekulla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-30 20:39:05

Otros han señalado correctamente el desajuste int/double en tu instrucción printf (). Solo quiero comentar su declaración, " Enlace con MSVCRT.LIB en lugar de LIBCMT para evitar el error de tiempo de ejecución R6002."Un programa así de simple no debería gravar indebidamente el entorno de tiempo de ejecución, por lo que un error de tiempo de ejecución como este debería ser una señal de alerta de comportamiento indefinido en su código.

Un rápido Google de "MSVCRT R6002" dice:

C Error en Tiempo de Ejecución R6002 soporte de coma flotante no cargado

La biblioteca de coma flotante necesaria no estaba vinculada. Para arreglar comprobando las siguientes posibles causas

  1. El programa se compiló o enlazó con una opción, como /FPi87, que requiere un coprocesador, pero el programa se ejecutó en una máquina que no tenía un coprocesador instalado.
  2. Una cadena de formato para una función printf_s o scanf_s contenía una especificación de formato de coma flotante y el programa no contenía ninguna valores o variables de coma flotante.
  3. El compilador minimiza el tamaño de un programa cargando soporte de punto flotante solo cuando es necesario. El compilador no puede detectar especificaciones de formato de punto flotante en cadenas de formato, por lo que no carga las rutinas de punto flotante necesarias.
  4. Utilice un argumento de coma flotante para corresponder a la especificación de formato de coma flotante, o realice una asignación de coma flotante en otra parte del programa. Esto causa el punto flotante soporte a cargar.
  5. En un programa de lenguaje mixto, se especificaba una biblioteca C antes que una biblioteca FORTRAN cuando el programa estaba vinculado. Vuelva a vincular y especifique la biblioteca de C en último lugar.

La lección aquí, por supuesto, es que debe prestar mucha atención a las advertencias y errores del compilador y del tiempo de ejecución. En caso de duda, siempre asuma que el problema está en su código, no en el del compilador.

 8
Author: Drew Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-01 11:03:24

En C porque especifica explícitamente el punto flotante ("%f") en su especificador de formato printf, por lo que espera un argumento de punto flotante. Pero le estás dando un argumento "int", de ahí el problema.

Dependiendo de lo que estés tratando de hacer, puedes:

1) Fundición de su (de lo contrario entero) expresión para flotar

Y / o

2) Usando setprecision en su flujo cout, tal como usaría "%.5f " en C:

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

3) Si quieres entero, use printf ("%d", 18/4+18%4);

 4
Author: paulsm4,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-08 08:44:28

Si quieres el mismo comportamiento (y respuesta) es mejor que codifiques

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

Para obtener una respuesta int en lugar de una en coma flotante. Obtendrá el mismo resultado que el operador << seleccionado es

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

De lo contrario, puede utilizar explícitamente

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

Para obtener 6.00000 resultado.

 2
Author: Luis Colorado,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-01 06:25:38

Haz que uno de tus números sea un valor en coma flotante:

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

    printf("%.0f", 18/4.0+18%4);
    return 0;
} 
 1
Author: smac89,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-30 20:16:40

Una posible alternativa es encasillar los literales (o la expresión misma):

#include <stdio.h>

int main(void)
{
    printf("%.5f", (float)18/4+18%4);
    return 0;
}
 0
Author: Dhruv Saxena,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-01 10:34:48