#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 поведение, делая то , что вы делаете, не будет много пользы. Вы могли бы попытаться изучить поведение в своей системе, и если да, вам следует прочитать документацию и посмотреть исходный код, чтобы узнать, доступен ли он для вашей библиотеки. printfprintfprintfprintf
Например, на моем 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аргументы. printfwith %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.