Почему этот вывод того же выражения из 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?
Ответы
Ответ 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;
}