Подтвердить что ты не робот

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

Ответ 3

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

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

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

В С++ std:: cout operator < < имеет перегрузку для int и поэтому в этом случае будет вызываться. Мы можем видеть эту перегрузку, многие другие требуются проект стандарта С++, в разделе 27.7.3.1 Шаблон класса basic_ostream мы находим следующее выражение оператора:

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

Для полноты, обращаясь к поведению undefined, черновик проекта C99 в разделе 7.19.6.1 Функция fprintf, которая printf ссылается на строку форматирования в абзаце 9:

Если спецификация преобразования недействительна, поведение undefined. [...]

Ответ 4

Выражение

18 / 4 + 18 % 4

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

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

С С++ и ostream, язык может автоматически определять тип вывода.

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

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

Ответ 5

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

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

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

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

  • Программа была скомпилирована или связана с опцией, например /FPi 87, для которой требуется сопроцессор, но программа была запущена на машине, на которой не было установлен сопроцессор.
  • Строка формата для функции printf_s или scanf_s содержала спецификацию формата с плавающей запятой, и программа не содержала никаких значений или переменных с плавающей запятой.
  • Компилятор минимизирует размер программы, загружая поддержку с плавающей запятой только тогда, когда это необходимо. Компилятор не может определить спецификации формата с плавающей запятой в строках формата, поэтому он не загружает необходимые процедуры с плавающей запятой.
  • Используйте аргумент с плавающей запятой, чтобы соответствовать спецификации формата с плавающей запятой, или выполнить назначение с плавающей запятой в другом месте программы. Это приводит к загрузке поддержки с плавающей запятой.
  • В программе на смешанном языке библиотека C была указана перед библиотекой FORTRAN при подключении программы. Переименуйте и укажите последнюю библиотеку C.

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

Ответ 6

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

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

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

и/или

2) Используя setprecision в вашем потоке cout, так же, как вы используете "%.5f" в 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);

Ответ 7

Если вам нужно одинаковое поведение (и ответ), вам лучше закодировать

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

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

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

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

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

чтобы получить результат 6.00000.

Ответ 8

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

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

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

Ответ 9

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

#include <stdio.h>

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