#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", a);
return 0;
}
Він відображає 0
!! Як це можливо? Що таке міркування?
Я навмисно дав %d
у printf
заяві знак вивчення поведінки printf
.
Відповіді:
Це тому, що %d
очікує, int
але ви надали поплавок.
Використовуйте %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
є варіадичною функцією, і стандарт говорить, що для варіадичних функцій а float
перетворюється на double
перед передачею.
printf()
викликах невизначеної поведінки .
Технічно не кажучи ні , кожна бібліотека реалізує свою власну, і , отже, ваш метод намагається дослідження «s поведінку, роблячи те , що ви робите, не буде багато користі. Можливо, ви намагаєтеся вивчити поведінку вашої системи, і якщо так, то прочитайте документацію та подивіться на вихідний код printf
printf
printf
printf
чи він доступний для вашої бібліотеки.
Наприклад, на моєму Macbook я отримую вихідні дані 1606416304
разом із вашою програмою.
Сказавши, що, коли ви передаєте float
a варіадичній функції, то float
передається як a 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
очікувати ціле число. Отже, перші чотири (або два, залежно від платформи) байти float інтерпретуються як ціле число. Якщо випадково вони дорівнюють нулю, друкується нуль
Бінарне представлення 1234,5 - це щось на зразок
1.00110100101 * 2^10 (exponent is decimal ...)
З компілятором C, який float
насправді представляє подвійні значення IEEE754, байти були б (якби я не помилився)
01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000
У системі Intel (x86) з невеликою віддачею (тобто найменш значущим байтом, який приходить першим), ця послідовність байтів змінюється таким чином, що перші чотири байти дорівнюють нулю. Тобто, що printf
роздруковує ...
Див. Цю статтю у Вікіпедії щодо подання з плаваючою комою згідно з IEEE754
Оскільки ви викликали невизначену поведінку: ви порушили контракт методу printf (), брешучи йому про його типи параметрів, тому компілятор може робити все, що заманеться. Це може зробити вихід програми "dksjalk - це нікчем !!!" і технічно це все одно буде правильно.
Причина в тому, що printf()
це досить німа функція. Він взагалі не перевіряє типи. Якщо ви говорите, що перший аргумент - це int
(і це те, з чим ви говорите %d
), він вірить вам і займає лише байти, необхідні для int
. У цьому випадку, якщо припустити, що ваша машина використовує чотирибайтові int
та восьмибайтові double
( float
перетворюється у double
внутрішню printf()
частину), перші чотири байти a
будуть просто нулями, і це друкується.
%d
є десятковою
%f
є плаваючим
дивіться більше з них тут .
Ви отримуєте 0, оскільки плаваючі та цілі числа представлені по-різному.
Вам просто потрібно використати відповідний специфікатор формату (% d,% f,% s тощо) з відповідним типом даних (int, float, рядок тощо).
Це не ціле число. Спробуйте використовувати %f
.