Ответ 1
Функция 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 это приводит к поведению undefined (§7.21.6.1/8-9), что включает в себя возможность печатать какой-то странный номер, конец истории.
Но что на самом деле происходит? На большинстве платформ double
представлен как IEEE 754 binary64 "и float
в формате binary32. Числа, которые вы ввели, преобразуются в float, который имеет только 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)
Теперь мы преобразуем это в double, который имеет 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 complement)
4 → ... 00000000 = 0
5 → ... 00000000 = 0