#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", a);
return 0;
}
Он отображает 0
!! Как это возможно? В чем причина?
Я специально поместил % d
в оператор printf
для изучения поведения printf
.
Это потому, что % d
ожидает int
, но вы указали число с плавающей запятой.
Используйте % e
/ % f
/ % g
, чтобы напечатать поплавок.
Почему печатается 0: число с плавающей запятой преобразуется в double
перед отправкой в printf
. Число 1234,5 в двойном представлении с прямым порядком байтов -
00 00 00 00 00 4A 93 40
A % d
использует 32-битное целое число, поэтому печатается ноль. (В качестве теста вы можете printf ("% d,% d \ n", 1234.5f);
Вы можете получить на выходе 0, 1083394560
.)
Что касается почему float
преобразуется в double
, поскольку прототип printf - int printf (const char *, ...)
, начиная с 6.5.2.2/7 ,
Использование многоточия в деклараторе прототипа функции вызывает остановку преобразования типа аргумента после последнего объявленного параметра. По умолчанию продвижение аргументов выполняется для конечных аргументов.
и из 6.5.2.2/6,
Если выражение, обозначающее вызываемую функцию, имеет тип, не включающий прототип, целочисленные повышения выполняются для каждого аргумента, и аргументов, имеющих тип
float
повышаются доdouble
. Это называется продвижением аргументов по умолчанию .
(Спасибо, Алок, за то, что это выяснил.)
Поскольку вы также пометили его тегом C ++, этот код выполняет преобразование как вы, вероятно, ожидаете:
#include <iostream.h>
int main() {
float a = 1234.5f;
std::cout << a << " " << (int)a << "\n";
return 0;
}
Вывод:
1234.5 1234
С технической точки зрения не существует the printf
, каждая библиотека реализует свою собственную, и, следовательно, ваш метод изучения поведения printf
, выполняя то же самое, что и вы. не принесет особой пользы. Вы могли бы попытаться изучить поведение printf
в вашей системе, и если да, вам следует прочитать документацию и посмотреть исходный код для printf
, если он доступен для вашей библиотека.
Например, на моем Macbook я получаю результат 1606416304
с вашей программой.
Сказав это, когда вы передаете float
вариативной функции, float
передается как double
. Итак, ваша программа эквивалентна объявлению a
как double
.
Чтобы проверить байты double
, вы можете увидеть этот ответ на недавний вопрос здесь, посвященный SO.
Давайте сделаем это:
#include <stdio.h>
int main(void)
{
double a = 1234.5f;
unsigned char *p = (unsigned char *)&a;
size_t i;
printf("size of double: %zu, int: %zu\n", sizeof(double), sizeof(int));
for (i=0; i < sizeof a; ++i)
printf("%02x ", p[i]);
putchar('\n');
return 0;
}
Когда я запускаю указанную выше программу, я получаю:
size of double: 8, int: 4
00 00 00 00 00 4a 93 40
Итак, первые четыре байта double
оказались равными 0, что может быть причиной того, что вы получили 0
как результат вашего вызова printf
.
Для получения более интересных результатов мы можем немного изменить программу:
#include <stdio.h>
int main(void)
{
double a = 1234.5f;
int b = 42;
printf("%d %d\n", a, b);
return 0;
}
Когда я запускаю указанную выше программу на моем Macbook, я получаю:
42 1606416384
С той же программой на машине Linux я получаю:
0 1083394560
Он не будет автоматически преобразовывать float в целое число. Потому что оба имеют разный формат хранения. Поэтому, если вы хотите преобразовать, используйте приведение типов (int).
#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", (int)a);
return 0;
}
эй, он должен был что-то напечатать, поэтому он напечатал 0. Помните, что в C 0 есть все остальное!
Вам просто нужно использовать соответствующий описатель формата (% d,% f,% s и т. Д.) С соответствующим типом данных (int, float, string и т. Д.).
% d
десятичное
% f
плавающее
подробнее см. здесь .
Вы получаете 0, потому что числа с плавающей запятой и целые числа представлены по-разному.
Причина в том, что printf()
- довольно глупая функция. Она вообще не проверяет типы. Если вы говорите, что первый аргумент - int
(а это то, что вы говорите с %d
), она вам верит и берет только байты, необходимые для int
. В этом случае, если предположить, что ваша машина использует четырехбайтовые int
и восьмибайтовые double
(float
преобразуется в double
внутри printf()
), первые четыре байта a
будут просто нулями, и это будет напечатано.
Спецификатор %d
указывает printf
ожидать целое число. Поэтому первые четыре (или два, в зависимости от платформы) байта float интерпретируются как целое число. Если они оказываются нулевыми, печатается ноль
Двоичное представление 1234.5 выглядит примерно так
1.00110100101 * 2^10 (exponent is decimal ...)
При использовании компилятора C, который представляет float
фактически как двойные значения IEEE754, байты будут (если я не ошибся)
01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000
В системе Intel (x86) с малой эндианальностью (т.е. младший байт идет первым) эта последовательность байтов меняется на противоположную, так что первые четыре байта равны нулю. То есть, то, что printf
выводит ...
См. эту статью Википедии для представления с плавающей запятой в соответствии с IEEE754.
Это из-за представления числа с плавающей запятой в двоичном формате. При преобразовании в целое число остается 0.
Потому что вы вызвали неопределенное поведение: вы нарушили договор с методом printf(), солгав ему о типах его параметров, поэтому компилятор волен делать все, что ему заблагорассудится. Он может заставить программу вывести "dksjalk is a ninnyhead!!!" и технически он все равно будет прав.