Почему этот вывод одного и того же выражения из printf отличается от cout?

Я использую Visual C++ 2012 и компилирую из командной строки следующие файлы:

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

связывание с MSVCRT.LIB, а не LIBCMT, чтобы избежать ошибки выполнения R6002.
Выходное значение для этой программы равно 0.00000.

однако, если я выполняю то же самое в C++

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

теперь он печатает 6, как и должно.

в чем разница? Это связано с самими языками (C vs C++) или с выходом методы (cout vs printf), или это просто причуда с MSVC?

9 ответов


выражение 18/4+18%4 оценивает в int, и вы запрашиваете поплавок. Вы всегда должны компилировать с включенными предупреждениями и обращать на них внимание (они говорят предупреждение-это ошибка ждет, и они правы).

это то, что мой компилятор (GCC 4.8.1) говорит мне (и даже без принуждения -Wall):

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

С другой стороны,std::cout<< операция способна вывести тип вашего выражения и правильно передать его в ваш экран.


функция C передается целым числом, но вы говорите это (с %f) ожидать числа с плавающей запятой двойной точности, поэтому он терпит неудачу. Функция C++ знает, что ей передается целое число, поэтому она работает правильно.


на C пример этого выражения 18/4+18%4 оценивают в int так как все операнды целочисленные константы но вы указываете, что это двойной to printf и поэтому он будет обработан неправильно. С другой стороны, если вы используете плавающие константы в части деления выражения, например 18.0/4+18%4 все выражение было бы оценено в a двойной. В качестве альтернативы вы могли бы использовать "%d" в спецификаторе формата.

это тоже неопределенное поведение неправильно указать формат в printf и это также демонстрирует, почему важно строить с предупреждениями, используя gcc -Wall Я получаю следующее предупреждение(видеть live):

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

на C++ std:: cout ' s оператор есть перегрузка для int и поэтому это будет называться в данном случае. Мы можем видеть эту перегрузку, и многие другие требуются проект стандарта C++, в секции 27.7.3.1 шаблон класса basic_ostream мы находим следующее объявление оператор:

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

для полноты, возвращаясь к неопределенному поведению,проект стандарта C99 в разделе 7.19.6.1 функция fprintf, который printf'ы раздел ссылается на строку формата paragraph 9 говорит:

если спецификация преобразования является недопустимым, поведение неопределено.[...]


выражение

18 / 4 + 18 % 4

оценивает в int.

но printf формат строки "%.5Ф" ожидает двойная.

С помощью c++ и ostreams язык может автоматически определять тип вывода.

просто измените свой C-код на следующий:

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

Другие правильно указали на несоответствие int/double в вашем операторе printf (). Я просто хочу прокомментировать ваше заявление", - говорится в сообщении MSVCRT.LIB, а не LIBCMT, чтобы избежать ошибки выполнения R6002."Такая простая программа не должна чрезмерно облагать налогом среду выполнения, поэтому такая ошибка выполнения должна быть красным флагом неопределенного поведения в вашем коде.

быстрый Google "MSVCRT R6002" говорит:

C Ошибка Времени Выполнения R6002 поддержка с плавающей запятой не загружена

необходимая библиотека с плавающей запятой не была связана. Исправить, проверив следующие возможные причины

  1. программа была скомпилирована или связана с опцией, такой как /FPi87, для которой требуется сопроцессор, но программа была запущена на машине, на которой не был установлен сопроцессор.
  2. строка формата для функции printf_s или scanf_s содержит формат с плавающей запятой спецификация и программа не содержали никаких значений или переменных с плавающей запятой.
  3. компилятор минимизирует размер программы, загружая поддержку с плавающей запятой только при необходимости. Компилятор не может обнаружить спецификации формата с плавающей запятой в строках формата, поэтому он не загружает необходимые процедуры с плавающей запятой.
  4. используйте аргумент с плавающей запятой для соответствия спецификации формата с плавающей запятой или выполните назначение с плавающей запятой в другом месте программы. Это приводит к загрузке поддержки с плавающей запятой.
  5. в программе на смешанном языке библиотека C была указана перед библиотекой FORTRAN, когда программа была связана. Повторно свяжите и укажите библиотеку C последней.

урок здесь, конечно, заключается в том, что вы должны уделять пристальное внимание предупреждениям компилятора и времени выполнения и ошибкам. Когда вы сомневаетесь, всегда предполагайте, что проблема в вашем коде, а не в компилятор.


В C, поскольку вы явно указываете плавающую точку ("%f") в описателе формата printf, поэтому он ожидает аргумент с плавающей точкой. Но вы даете ему аргумент "int", следовательно, проблема.

в зависимости от того, что вы пытаетесь сделать, вы можете:

1) литье (в противном случае целое) выражение для float

и/или

2) с помощью setprecision в поток cout, так же, как вы бы использовать "%.5Ф" в C:

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

3) Если вы хочу целое число, используйте printf ("%d", 18/4+18%4);


если вы хотите такое же поведение (и ответ) вы бы лучше код

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

чтобы получить ответ int Вместо ответа с плавающей запятой. Вы получите тот же результат, что и << выбранный оператор

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

в противном случае, вы можете использовать явно

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

и 6.00000 результат.


пусть одно из ваших чисел будет значением с плавающей запятой:

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

    printf("%.0f", 18/4.0+18%4);
    return 0;
} 

одной из возможных альтернатив является типизация литералов (или самого выражения):

#include <stdio.h>

int main(void)
{
    printf("%.5f", (float)18/4+18%4);
    return 0;
}