#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", a);
return 0;
}
Он отображает 0
!! Как такое возможно? В чем причина?
Я намеренно поставил %d
в printf
утверждение, чтобы изучить поведение printf
.
Ответы:
Это потому, что %d
ожидает, int
но вы предоставили float.
Используйте %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
. Они называются продвижением аргументов умолчанию .
(Спасибо Алоку за то, что узнал об этом.)
printf
это функция с переменным числом аргументов, а в стандарте сказано, что для функций с переменным числом аргументов a перед передачей float
преобразуется в double
.
printf()
вызывает неопределенное поведение .
Технически не говоря нет , каждая библиотека реализует свою собственную, и , следовательно, ваш метод пытается исследования «s поведение, делая то , что вы делаете, не будет много пользы. Вы могли бы попытаться изучить поведение в своей системе, и если да, вам следует прочитать документацию и посмотреть исходный код, чтобы узнать, доступен ли он для вашей библиотеки. printf
printf
printf
printf
Например, на моем Macbook я получаю результат 1606416304
с вашей программой.
Сказав это, когда вы передаете float
переменную функцию, float
она передается как double
. Итак, ваша программа эквивалентна объявлению в a
виде файла double
.
Чтобы проверить байты a 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
int
аргументы передаются в разных регистрах, чем double
аргументы. printf
with %d
принимает int
аргумент, равный 42, а второй, %d
вероятно, печатает мусор, поскольку второго int
аргумента не было.
Спецификатор %d
говорит printf
ожидать целое число. Таким образом, первые четыре (или два, в зависимости от платформы) байта числа с плавающей запятой интерпретируются как целое число. Если они равны нулю, печатается ноль.
Двоичное представление 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 - тупица !!!" и технически это все равно было бы правильно.
Причина в том, что printf()
это довольно глупая функция. Он вообще не проверяет типы. Если вы говорите, что первый аргумент - это int
(и это то, что вы говорите %d
), он верит вам и берет только байты, необходимые для int
. В этом случае, если ваша машина использует четырехбайтовые int
и восьмибайтовые байты double
( float
преобразуется во double
внутреннюю printf()
), первые четыре байта a
будут просто нулями, и это будет напечатано.
%d
десятичный
%f
плавает
см. больше здесь .
Вы получаете 0, потому что числа с плавающей запятой и целые числа представлены по-разному.
Вам просто нужно использовать соответствующий описатель формата (% d,% f,% s и т. Д.) С соответствующим типом данных (int, float, string и т. Д.).
эй, он должен был что-то напечатать, поэтому он напечатал 0. Помните, что в C 0 все остальное!
Это не целое число. Попробуйте использовать %f
.