Неверный вывод из printf числа - PullRequest
2 голосов
/ 25 августа 2010
int main()
{
       double i=4;
       printf("%d",i);
       return 0;
}

Кто-нибудь может сказать мне, почему эта программа выдает 0?

Ответы [ 8 ]

19 голосов
/ 25 августа 2010

Когда вы создаете double, инициализированный значением 4, его 64 бита заполняются в соответствии со стандартом IEEE-754 для чисел с плавающей запятой двойной точности. Поплавок делится на три части: знак , показатель и дробь (также известный как значим , коэффициент или мантисса ). Знак является одним битом и обозначает, является ли число положительным или отрицательным. Размеры других полей зависят от размера числа. Для декодирования числа используется следующая формула:

1. Фракция & раз; 2 Экспонент - 1023

В вашем примере бит знака равен 0, поскольку число положительное, дробная часть равна 0, поскольку число инициализируется как целое число, а часть экспоненты содержит значение 1025 (2 со смещением 1023) , Результат:

1,0 & раз; 2 2

Или, как и следовало ожидать, 4. Двоичное представление числа (разделенное на разделы) выглядит следующим образом:

0 10000000001 0000000000000000000000000000000000000000000000000000

Или, в шестнадцатеричном, 0x4010000000000000. При передаче значения в printf с использованием спецификатора %d он пытается прочитать sizeof(int) байтов из переданных ему параметров. В вашем случае sizeof(int) - это 4 или 32 бита. Поскольку все первые (самые правые) 32 бита 64-битного числа с плавающей запятой, которые вы предоставляете, равны 0, очевидно, что printf выдает 0 в качестве целочисленного вывода. Если бы вы написали:

printf("%d %d", i);

Тогда вы можете получить 0 1074790400, где второе число эквивалентно 0x40100000. Я надеюсь, вы понимаете, почему это происходит. Другие ответы уже дали исправление: используйте спецификатор формата %f, и printf правильно примет ваш double.

8 голосов
/ 26 августа 2010

Джон Пурди дал вам замечательное объяснение того, почему вы видели именно этот результат. Однако имейте в виду, что поведение явно undefined в соответствии со стандартом языка:

7.19.6.1.9: Если спецификация преобразования недопустима, поведение не определено. 248) Если какой-либо аргумент не является правильным типом для соответствующей спецификации преобразования, поведение не определено,

(выделено мной), где "неопределенное поведение" означает

3.4.3.1: поведение при использовании непереносимой или ошибочной программной конструкции или ошибочных данных, для которых настоящий международный стандарт не предъявляет никаких требований

IOW, компилятор не обязан производить значимый или правильный результат. Самое главное, вы не можете полагаться на результат повторяемости. Нет гарантии, что эта программа выдаст 0 на других платформах или даже на той же платформе с другими настройками компилятора (это будет , вероятно, , но вы не хотите на это полагаться).

3 голосов
/ 25 августа 2010

% d для целых чисел:

int main()
{

   int i=4;
   double f = 4;
   printf("%d",i); // prints 4
   printf("%0.f",f); // prints 4
   return 0;
}
2 голосов
/ 25 августа 2010

Потому что язык позволяет вам облажаться, и вы счастливо делаете это.

В частности, «% d» - это форматирование для int, и поэтому printf («% d») потребляет столько байтов из аргументов, сколько принимает int. Но double намного больше, поэтому printf получает только кучу нулей. Используйте "% lf".

1 голос
/ 25 августа 2010

Простой ответ на ваш вопрос *1001*, как уже говорили другие, говорит printf напечатать целое число (например, переменную типа int) при передаче ему числа двойной точности (поскольку ваша переменная имеет тип double), что неверно.

Вот фрагмент кода от printf(3) программиста linuxруководство, объясняющее спецификаторы преобразования %d и %f :

   d, i   The int argument is converted to signed decimal  notation.   The
          precision,  if any, gives the minimum number of digits that must
          appear; if the converted value  requires  fewer  digits,  it  is
          padded  on  the  left  with  zeros.  The default precision is 1.
          When 0 is printed with an explicit precision 0,  the  output  is
          empty.

   f, F   The double argument is rounded and converted to decimal notation
          in the style [-]ddd.ddd, where the number of  digits  after  the
          decimal-point character is equal to the precision specification.
          If the precision is missing, it is taken as 6; if the  precision
          is  explicitly  zero,  no decimal-point character appears.  If a
          decimal point appears, at least one digit appears before it.

Чтобы ваш текущий код работал, вы можете сделать две вещи.Первый вариант уже был предложен - заменить %d на %f.

Другая вещь, которую вы можете сделать, это привести ваше double к int, например:

printf("%d", (int) i);

Более сложный ответ (объясняющий, почемуprintf ведет себя так же, как и он), только что кратко ответил Джон Пурди.Для более подробного объяснения взгляните на статью в Википедии, касающуюся арифметики с плавающей запятой и двойной точности .

1 голос
/ 25 августа 2010

Поскольку "%d" указывает, что вы хотите напечатать int, но i - это double.Вместо этого попробуйте printf("%f\n"); (\n указывает символ новой строки).

0 голосов
/ 25 августа 2010

@ jagan относительно подвопроса:

Что является самым левым третьим байтом.Почему это 00000001?Может кто-нибудь объяснить? "

10000000001 для 1025 в двоичном формате.

0 голосов
/ 25 августа 2010

Потому что я двойник, и вы указываете printf использовать его, как если бы он был int (% d).

...