Chức năng printf
không biết loại định dạng bạn đã chuyển, vì phần đó là variadic.
int printf(const char* format, ...);
// ^^^
Trong tiêu chuẩn C, đi qua một float
sẽ tự động thăng một double
(C11§6.5.2.2/6), và không có gì khác sẽ được thực hiện ở phía người gọi.
Bên trong printf
, vì nó không biết loại ...
thingie (§6.7.6.3/9), nó phải sử dụng gợi ý từ nơi khác - chuỗi định dạng. Vì bạn đã vượt qua "%d"
, nó đang cho biết chức năng đó, một dự kiến là int
.
Theo tiêu chuẩn C, điều này dẫn đến hành vi không xác định (§7.21.6.1/8–9), bao gồm khả năng in một số số lạ, kết thúc câu chuyện.
Nhưng điều gì đang thực sự xảy ra? Trong hầu hết các nền tảng, một double
được thể hiện dưới dạng "IEEE 754 binary64" và định dạng float
ở định dạng binary32. Những con số mà bạn đã nhập không chuyển đổi sang một phao, mà chỉ có 23 bit có ý nghĩa, có nghĩa là những con số sẽ được xấp xỉ như thế này:
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)
Bây giờ chúng ta chuyển đổi này sẽ tăng gấp đôi, trong đó có 53 bit có ý nghĩa , chúng ta phải chèn 30 số nhị phân "0" vào cuối những con số này, để tạo ra ví dụ
3.299999952316284 = 0b1.10100110011001100110011000000000000000000000000000000 ×2¹
Đây là chủ yếu để lấy được các đại diện thực tế của những con số, đó là:
3.3 → 400A6666 60000000
3.4 → 400B3333 40000000
3.5 → 400C0000 00000000
3.6 → 400CCCCC C0000000
4 → 40100000 00000000
5 → 40140000 00000000
tôi khuyên bạn nên sử dụng http://www.binaryconvert.com/convert_double.html để xem cách này phá vỡ xuống ± m × 2 e Định dạng.
Dù sao, tôi cho rằng hệ thống của bạn là một x86/x86_64/ARM trong khung cảnh bình thường, có nghĩa là con số được đưa ra trong bộ nhớ sử dụng little-endian format, vì vậy các đối số được truyền sẽ như thế nào
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)
Bên trong printf
, nó tiêu thụ các chuỗi định dạng "%d"
, phân tích nó, và sau đó phát hiện ra rằng một int
là cần thiết vì% d, vì vậy 4 byte được lấy từ đầu vào variadic, đó là:
byte
#0 #1 ... #4 ... #8 ....
+ - -+ - -+ - -+ - -+ +====+====+====+====+ - -+ - -+ - -+ - -+
: 08 : 10 : 02 : 00 : | 00 | 00 | 00 | 60 | 66 : 66 : 0A : 40 : ....
+ - -+ - -+ - -+ - -+ +====+====+====+====+ - -+ - -+ - -+ - -+
address of "%d" ~~~~~~~~~~~~~~~~~~~
this, as an 'int'
vậy, printf sẽ nhận được 0x60000000 và hiển thị nó dưới dạng số nguyên thập phân, là 1610612736, đó là lý do tại sao bạn thấy kết quả đó. Các số khác có thể được giải thích tương tự.
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
+1 dành thời gian để viết tất cả điều này. :) – Mysticial
Để hoàn thiện, một vài liên kết bổ sung hữu ích của Hai, [Hai bổ sung - Wikipedia] (http://en.wikipedia.org/wiki/Two%27s_complement) và [Hai bổ sung ghi chú Thomas Finley] (http: // tfinley.net/notes/cps104/twoscomp.html). – mctylr