printf
函数不知道您传入的格式类型,因为这部分是可变的。
int printf(const char* format, ...);
// ^^^
在C标准中,传递一个
float
类型的参数将自动提升为
double
(C11§6.5.2.2/6),在调用方不会进行其他操作。
在
printf
内部,由于它不知道那个
...
东西的类型(§6.7.6.3/9),因此必须使用来自其他地方的提示——格式字符串。由于您传递了
"%d"
,它告诉函数,期望一个
int
类型的参数。
根据C标准,这会导致未定义的行为(§7.21.6.1/8-9),其中包括打印一些奇怪的数字,结束故事。
但是,
实际上发生了什么? 在大多数平台上,
double
采用"
IEEE 754二进制64位浮点格式"表示,而
float
则采用
二进制32位浮点格式。您输入的数字被转换为浮点数,在这里只有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 × 2e格式的。
不过,我猜你的系统通常是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"
,解析该字符串,然后通过 %d 发现需要一个 int
,因此会从可变输入中获取 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