Как работает эта программа?

#include <stdio.h>

int main() {
    float a = 1234.5f;
    printf("%d\n", a);
    return 0;
}

выводит 0!! Как это возможно? В чем причина?


Я намеренно поставил%d на printf заявление для изучения поведения printf.

13 ответов


потому что %d ждет int но вы предоставили поплавок.

использовать %e/%f/%g для печати поплавка.


о том, почему печатается 0: число с плавающей запятой преобразуется в double перед отправкой в printf. Число 1234.5 в двойном представлении в little endian равно

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, каждая библиотека реализует свой собственный, а значит, свой метод изучения 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

на %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) с мало endianess (т. е. младший байт идет первым), эта последовательность байт развернется так, что первые четыре байта равны нулю. То есть, что printf распечатывает ...

посмотреть эта статья в Википедии для представления с плавающей запятой в соответствии с IEEE754.


Это из-за представление float в двоичный. Преобразование в целое число оставляет его с 0.


потому что вы вызвали неопределенное поведение: вы нарушили контракт метода printf (), солгав ему о его типах параметров, поэтому компилятор может делать все, что пожелает. Это может сделать программу вывода "dksjalk является ninnyhead!!!- и технически это было бы правильно.


причина в том, что printf() довольно глупая функция. Он вообще не проверяет типы. Если вы говорите, что первый аргумент -int (и это то, что вы говорите с %d), Он верит вам, и он принимает только байты, необходимые для int. В этом случае asuming вашей машины использует четырехбайтовый int и восемь байт double (the float превращается в double внутри printf()), первые четыре байта a будут просто нули, и это будет напечатано.


он не будет автоматически преобразовывать float в integer. Потому что оба имеют разный формат хранения. Поэтому, если вы хотите преобразовать, используйте (int) typecasting.

#include <stdio.h>

int main() {
    float a = 1234.5f;
    printf("%d\n", (int)a);
    return 0;
}

поскольку вы также пометили его C++,код делает преобразование, как вы, вероятно, ожидаете:

#include <iostream.h>

int main() {
    float a = 1234.5f;
    std::cout << a << " " << (int)a << "\n";
    return 0;
}

выход:

1234.5 1234

%d является decimal

%f это float

это здесь.

вы получаете 0, потому что поплавки и целые числа представляются по-разному.


вам просто нужно использовать соответствующий спецификатор формата (%d,%f,%s и т. д.) с соответствующим типом данных (int, float, string и т. д.).


вы хотите %f не %d


Эй, он должен был что-то напечатать, поэтому он напечатал 0. Помните, что в C 0 все остальное!


Это не целое число. Попробуйте использовать %f.