Функция printf
не знает тип формата, который вы передали, потому что эта часть является переменной.
int printf(const char* format, ...);
// ^^^
В стандарте C передача float
будет автоматически повышена доdouble
(C11§6.5.2.2 / 6), и больше ничего не будет сделано на стороне вызывающего абонента.
Внутри printf
, так как он не знает тип этого ...
thingie (§6.7.6.3 / 9), он должен использовать подсказку из другого места - строку формата.Поскольку вы передали "%d"
, функция сообщает, что ожидается int
.
Согласно стандарту C, это приводит к неопределенному поведению (§7.21.6.1 / 8–9), что включает в себя возможность печати какого-то странного числа, конец истории.
Но что на самом деле происходит? На большинстве платформ double
представлен в формате " IEEE 754 binary64 " и в формате float
в binary32 .Числа, которые вы ввели, конвертируются в число с плавающей точкой, значение которого имеет только 23 бита, что означает, что числа будут приблизительно такими:
3.3 ~ (0b1.10100110011001100110011) × 2¹ (actually: 3.2999999523162842...)
3.4 ~ (0b1.10110011001100110011010) × 2¹ (actually: 3.4000000953674316...)
3.5 = (0b1.11 ) × 2¹ (actually: 3.5)
3.6 ~ (0b1.11001100110011001100110) × 2¹ (actually: 3.5999999046325684...)
4 = (0b1 ) × 2² (actually: 4)
5 = (0b1.01 ) × 2² (actually: 5)
Теперь мы конвертируем это в двойное число, которое имеет 53 бита.значение, которое мы должны вставить 30 двоичных «0» в конце этих чисел, например,
3.299999952316284 = 0b1.10100110011001100110011000000000000000000000000000000 ×2¹
Они в основном для получения фактического представления тех чисел, которые являются:
3.3 → 400A6666 60000000
3.4 → 400B3333 40000000
3.5 → 400C0000 00000000
3.6 → 400CCCCC C0000000
4 → 40100000 00000000
5 → 40140000 00000000
Я рекомендую использовать http://www.binaryconvert.com/convert_double.html, чтобы увидеть, как это можно сделать до формата ± m × 2 e .
В любом случае, я предполагаю, что ваша система представляет собой x86 / x86_64 / ARM в нормальном режиме, что означает, что числа расположены в памяти, используя формат с прямым порядком байтов , поэтому переданные аргументыкак
byte
#0 #1 ... #4 ... #8 ....
+----+----+----+----+ +----+----+----+----+----+----+----+----+
| 08 | 10 | 02 | 00 | | 00 | 00 | 00 | 60 | 66 | 66 | 0A | 40 | ....
+----+----+----+----+ +----+----+----+----+----+----+----+----+
address of "%d" content of 3.299999952316284
(just an example)
Внутри printf
он использует строку формата "%d"
, анализирует ее и затем обнаруживает, что необходим int
из-за% d, поэтому 4 байта берутся изпеременный ввод, который:
byte
#0 #1 ... #4 ... #8 ....
+ - -+ - -+ - -+ - -+ +====+====+====+====+ - -+ - -+ - -+ - -+
: 08 : 10 : 02 : 00 : | 00 | 00 | 00 | 60 | 66 : 66 : 0A : 40 : ....
+ - -+ - -+ - -+ - -+ +====+====+====+====+ - -+ - -+ - -+ - -+
address of "%d" ~~~~~~~~~~~~~~~~~~~
this, as an 'int'
, так что printf получит 0x60000000 и отобразит его как десятичное целое число, равное 1610612736, поэтому вы видите этот результат.Другие числа можно объяснить аналогично.
3.3 → ... 60000000 = 1610612736
3.4 → ... 40000000 = 1073741824
3.5 → ... 00000000 = 0
3.6 → ... C0000000 = -1073741824 (note 2's complement)
4 → ... 00000000 = 0
5 → ... 00000000 = 0