Module 1 — Tổng kết & cheat sheet biểu diễn dữ liệu
Recap Module 1: bit/byte/hex, two's complement, IEEE 754, UTF-8, endianness và thao tác bit. Cheat sheet, glossary, pitfall và self-assessment để bookmark.
TL;DR: Module 1 bóc 5 quy ước mã hoá nền tảng mà mọi kiểu dữ liệu trong máy tính đều xây trên đó. Đây là 1 trang để bookmark — cheat sheet, glossary và pitfall tổng hợp để tra nhanh khi cần.
Đã đi qua những gì
Bài 01 đặt nền với bit, byte và hệ cơ số: máy chọn nhị phân vì phần cứng phân biệt tin cậy hai mức điện áp, còn hex chỉ là cách viết tắt gọn cho người (2^4 = 16 nên 1 chữ số hex = 4 bit). Bài 02 xây trên đó để giải thích số nguyên có dấu: two's complement cho phép cộng trừ dùng chung một mạch, nhưng cũng là nguyên nhân tràn số wrap-around khi kết quả vượt giới hạn n bit. Bài 03 chuyển sang số thực IEEE 754 — lý do 0.1 + 0.2 ra 0.30000000000000004 nằm ở chỗ 0.1 không biểu diễn chính xác trong nhị phân, giống 1/3 không viết được chính xác trong thập phân. Bài 04 rời thế giới số sang văn bản: Unicode gán code point cho mọi ký tự trên đời, UTF-8 mã hoá chúng thành 1–4 byte theo cơ chế prefix, và mojibake xảy ra khi byte được đọc bằng sai encoding. Bài 05 khép lại với endianness — thứ tự byte khi lưu số nhiều byte — và bộ thao tác bit-level (mask, shift, flag) thực dụng cho lập trình hằng ngày.
Sơ đồ dưới tóm tắt chuỗi 5 bài và pitfall trung tâm của từng bài:
flowchart LR A["Bit/byte/hex<br/>2^n gia tri"] --> B["So nguyen<br/>tran so wrap"] B --> C["IEEE 754<br/>float != chinh xac"] C --> D["UTF-8<br/>1 ky tu != 1 byte"] D --> E["Endianness<br/>+ bitmask"]
Cheat sheet
| Khái niệm | Ý chính | Pitfall |
|---|---|---|
n bit, 2^n giá trị | 8 bit = 256 giá trị (0–255); 32 bit = hơn 4 tỷ | Chọn kiểu quá nhỏ → tràn; quá lớn → phí bộ nhớ |
| Two's complement | Bit cao nhất là dấu; đảo bit + 1 ra số âm | Giá trị âm nhỏ nhất không có đối xứng dương (INT_MIN không negation được) |
| Overflow | Kết quả vượt giới hạn → wrap-around, không raise error mặc định | Kết quả âm từ phép cộng hai số dương là dấu hiệu tràn |
| IEEE 754 | float 32 bit (1 dấu + 8 exp + 23 mantissa); double 64 bit | Phép so sánh == với float không đáng tin; dùng epsilon |
| Float cho tiền | Sai lầm kinh điển — float không biểu diễn chính xác phần thập phân nhị phân | Dùng BigDecimal (Java), decimal (Python/C#) cho tiền tệ |
| Code point vs byte | Code point là số định danh ký tự trong Unicode; byte là cách mã hoá thực tế | Len của string ≠ len byte khi có ký tự ngoài ASCII |
| UTF-8 | ASCII tương thích (1 byte); ký tự mở rộng 2–4 byte, prefix 10xxxxxx | Đọc file mà không chỉ rõ charset UTF-8 → mojibake |
| Mojibake | Byte UTF-8 bị đọc bằng encoding khác (Latin-1, Windows-1252...) → ký tự lạ | Tiếng Việt hiện Tiếng Viá»t thường là UTF-8 bị đọc Latin-1 |
| Endianness | Big-endian: byte cao trước; little-endian: byte thấp trước | x86 little-endian; network byte order big-endian — phải convert khi đọc binary protocol |
| Bitmask | value & mask giữ lại bit muốn đọc; value | flag bật bit | Quên & 0xFF khi ép byte sang int → sign extension gây giá trị âm |
Glossary
| Thuật ngữ | Định nghĩa 1 câu |
|---|---|
| bit | Đơn vị thông tin nhỏ nhất, chứa giá trị 0 hoặc 1. |
| byte | 8 bit gộp lại, biểu diễn được 256 giá trị khác nhau (0–255). |
| hex | Hệ cơ số 16, dùng ký hiệu 0–9 và A–F; 1 chữ số hex tương đương đúng 4 bit. |
| two's complement | Quy ước mã hoá số nguyên có dấu: đảo tất cả bit rồi cộng 1 để ra số âm, giúp cộng/trừ dùng chung mạch. |
| overflow | Kết quả phép toán vượt dải giá trị của kiểu dữ liệu, dẫn đến wrap-around thay vì báo lỗi. |
| mantissa | Phần định trị trong biểu diễn IEEE 754, quyết định độ chính xác của số thực. |
| exponent | Phần số mũ trong biểu diễn IEEE 754, xác định quy mô (magnitude) của số. |
| NaN | "Not a Number" — giá trị IEEE 754 đặc biệt xuất hiện khi phép toán không có kết quả hợp lệ (vd: 0.0 / 0.0). |
| code point | Số định danh duy nhất của một ký tự trong bảng Unicode, ký hiệu U+XXXX. |
| UTF-8 | Encoding biến độ dài mã hoá code point Unicode thành 1–4 byte, tương thích ngược với ASCII. |
| mojibake | Chuỗi ký tự lạ xuất hiện khi byte văn bản được đọc bằng encoding sai với encoding lúc ghi. |
| endianness | Thứ tự byte khi lưu số nguyên nhiều byte vào bộ nhớ: big-endian (byte cao trước) hoặc little-endian (byte thấp trước). |
| bitmask | Giá trị nhị phân dùng để chọn ra (AND) hoặc bật/tắt (OR/XOR) một tập bit cụ thể trong một số nguyên. |
| sign extension | Hiện tượng bit dấu được nhân rộng khi mở rộng kiểu số có dấu, ví dụ byte -1 (0xFF) thành int -1 (0xFFFFFFFF). |
Pitfall tổng hợp
Pitfall 1: So sánh float bằng ==
// Wrong -- floating-point is imprecise
double a = 0.1 + 0.2;
if (a == 0.3) System.out.println("equal"); // never prints
// Correct -- compare with epsilon tolerance
double a = 0.1 + 0.2;
double epsilon = 1e-9;
if (Math.abs(a - 0.3) < epsilon) System.out.println("equal"); // works
Pitfall 2: Dùng float/double cho tiền tệ
// Wrong -- monetary rounding error accumulates
double price = 0.1;
double total = 0;
for (int i = 0; i < 10; i++) total += price;
System.out.println(total); // 0.9999999999999999
// Correct -- use BigDecimal for money
import java.math.BigDecimal;
BigDecimal price = new BigDecimal("0.1");
BigDecimal total = BigDecimal.ZERO;
for (int i = 0; i < 10; i++) total = total.add(price);
System.out.println(total); // 1.0
Pitfall 3: Tràn số int không báo lỗi
// Wrong -- silent overflow, no exception
int x = Integer.MAX_VALUE; // 2_147_483_647
int y = x + 1; // wraps to -2_147_483_648
System.out.println(y); // -2147483648 (?!)
// Correct -- use Math.addExact to throw on overflow, or switch to long
int x = Integer.MAX_VALUE;
int y = Math.addExact(x, 1); // throws ArithmeticException
// Or: long y = (long) x + 1;
Pitfall 4: Đọc file không chỉ rõ charset UTF-8
// Wrong -- relies on platform default charset (may not be UTF-8)
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
// Correct -- always specify charset explicitly
BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream("data.txt"), StandardCharsets.UTF_8)
);
Pitfall 5: Quên & 0xFF khi đọc byte thành int
// Wrong -- sign extension turns 0xFF into -1
byte b = (byte) 0xFF;
int val = b; // val = -1, not 255
// Correct -- mask off sign extension
byte b = (byte) 0xFF;
int val = b & 0xFF; // val = 255
Pitfall 6: Nhầm endianness khi đọc binary protocol
// Wrong -- reading little-endian data as big-endian (or vice versa)
DataInputStream dis = new DataInputStream(socket.getInputStream());
int value = dis.readInt(); // DataInputStream always reads big-endian
// If the sender wrote little-endian, value is byte-swapped
// Correct -- use ByteBuffer with explicit byte order
byte[] raw = socket.getInputStream().readNBytes(4);
int value = ByteBuffer.wrap(raw).order(ByteOrder.LITTLE_ENDIAN).getInt();
Pitfall 7: Nhầm length() của String với số byte
// Wrong -- assumes 1 char = 1 byte
String s = "Việt";
int byteCount = s.length(); // 4 -- counts Java chars, not bytes
// Correct -- get actual UTF-8 byte count
String s = "Việt";
int byteCount = s.getBytes(StandardCharsets.UTF_8).length; // 7 bytes
Self-assessment
Bạn đã đạt Module 1 nếu trả lời được:
- Giải thích được bit, byte và cách đọc nhị phân, hex — nếu chưa chắc, đọc lại bài 01 section "Bit và byte" + "Hex: cách viết tắt cho người".
- Dự đoán được khi nào phép tính tràn số và kết quả wrap-around ra sao với two's complement — nếu chưa chắc, đọc lại bài 02 section "Two's complement" + "Tràn số".
- Giải thích được vì sao
0.1 + 0.2không bằng0.3và khi nào không được dùng float — nếu chưa chắc, đọc lại bài 03 section "Vì sao 0.1 không biểu diễn chính xác" + "Float cho tiền". - Truy vết được cách UTF-8 mã hoá ký tự Unicode thành byte và chẩn đoán được lỗi font (mojibake) — nếu chưa chắc, đọc lại bài 04 section "UTF-8 mã hoá thế nào" + "Mojibake".
- Implement được vài thao tác bit-level: bitmask để đọc flag, shift để nhân/chia luỹ thừa 2 — nếu chưa chắc, đọc lại bài 05 section "Bitmask và flag" + "Shift".
What's next
Bạn vừa học cách dữ liệu được biểu diễn — số nguyên, số thực, văn bản, thứ tự byte đều là quy ước mã hoá bit. Câu hỏi tiếp theo tự nhiên là: CPU xử lý đống bit đó như thế nào? Module 2 "Máy chạy thế nào" sẽ giải thích vòng lặp fetch-decode-execute, kiến trúc thanh ghi, stack, heap và cách instruction set biến code cấp cao thành thứ CPU thực sự chạy.
Tài liệu mở rộng
- Code: The Hidden Language of Computer Hardware and Software — Charles Petzold. Cuốn sách xây từng tầng từ công tắc điện đến máy tính hoàn chỉnh, giải thích bit/byte/logic gate theo cách trực quan nhất. Không cần nền tảng toán — chỉ cần tò mò.
- What Every Computer Scientist Should Know About Floating-Point Arithmetic — David Goldberg (Oracle/Sun, 1991). Paper kinh điển giải thích IEEE 754 tường tận: rounding modes, cancellation, error accumulation. Đọc khi cần debug float bug phức tạp hoặc implement numerical algorithm.
- The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) — Joel Spolsky (2003). Bài blog giải thích encoding từ ASCII đến Unicode/UTF-8 theo ngôn ngữ dev thực tế, không học thuật. Đọc một lần là hết nhầm mojibake.
Bài tiếp theo: Máy chạy thế nào — tổng quan
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên