2010-03-04 2 views
88
#include <stdio.h> 

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

Он отображает 0 !! Как это возможно? Что такое рассуждения?Как работает эта программа?


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

ответ

238

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

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


О том, почему 0 печатается: Число с плавающей запятой преобразуется в double перед отправкой printf. Число 1234,5 в двойном представлении в маленьком Endian является

00 00 00 00 00 4A 93 40 

%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. Они называются рекламными акциями по умолчанию.

(. Спасибо ALOK для обнаружения этого)

+17

+1, отличный ответ :) – Mizipzor

+4

+1 Лучший ответ. Это отвечает как «стандартным технически правильным», так и «вашей вероятной реализации», почему. –

+12

Я считаю, что вы единственный из 12 человек, которые фактически предоставили ответ, который он искал. – Gabe

0

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

+2

Да, я знаю. Я специально поставил '% d'. – Lazer

+0

По какой причине? – tangrs

+0

Я думаю, вопрос в том, почему он просто не конвертирует float в int и не отображает «1234»? –

1

%d десятичный

%f является поплавок

видеть больше этих here.

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

6

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

+1

Вы, кажется, единственный, кто понял, о чем просил. Если я не ошибаюсь, конечно. – Mizipzor

+0

Нет, он в любом случае ошибочен –

+0

Я согласен с тем, что ответ является неточным и неполным, но не ошибочным. – Shaihi

20

Спецификатор %d сообщает printf, чтобы ожидать целое число. Таким образом, первые четыре (или два, в зависимости от платформы) байта float intepreted как целое число. Если они происходят равными нулю, ноль печатается

Двоичное представление 1234.5 нечто вроде

1.00110100101 * 2^10 (exponent is decimal ...) 

с компилятором, который представляет float фактически в виде двойных значений IEEE754, байты будет (если я не ошиблась)

01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000 

На x86) системы Intel (с небольшим количеством порядок байтов (т.е. младший байт идет первым), эта последовательность байт изменяется на противоположную, так что первые четыре байта равны нулю. То есть, что printf распечатывает ...

См. This Wikipedia article для представления с плавающей точкой в ​​соответствии с IEEE754.

5

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

7

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

3

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

#include <stdio.h> 

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

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

#include <iostream.h> 

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

Выход:

1234.5 1234 
45

Технически говоря, нет printf, каждая библиотека реализует свою собственную, и поэтому ваш метод изучения поведения printf, делая то, что вы делаете, не будет очень полезен. Вы можете попытаться изучить поведение printf в вашей системе, и если да, прочитайте документацию и посмотрите исходный код для printf, если он доступен для вашей библиотеки.

Например, на моем Macbook я получаю вывод 1606416304 с вашей программой.

Сказав это, когда вы передаете float на вариационную функцию, float передается как double.Таким образом, ваша программа эквивалентна объявлению a как double.

Чтобы изучить байты double, вы можете увидеть this answer на недавний вопрос здесь о 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 
+1

+1, кажется, лучший ответ, чем тот, который был принят. – tipycalFlow

+0

Почему вы программировали печать назад? Я что-то упускаю? Если b - это int = 42, а '% d' - целочисленный формат, почему это не второе печатное значение, так как это вторая переменная в аргументах printf? Это опечатка? –

+0

Возможно, потому что аргументы 'int' передаются в разных регистрах, чем аргументы' double'. 'printf' с'% d' принимает аргумент 'int', который равен 42, а второй'% d', вероятно, печатает мусор, поскольку не было второго аргумента 'int'. –

0

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

+1

Как это так? В C нет такой вещи, как все остальное. – Vlad

+0

если x - это что-то, то! X == 0 :) – chunkyguy

+0

if (iGot == "все") print ** "все"; else ** печатать «ничего»; – nik

1

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

+0

Вопрос не в том, как это исправить, а в том, почему он работает так, как он работает. – ya23

Смежные вопросы