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.
Bảng dưới tra theo chiều bạn gặp vấn đề — từ triệu chứng trên màn hình ngược về quy ước gây ra nó:

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
Byte order và thao tác bit — endianness, mask, shift
Bài tiếp Máy chạy thế nàoModule 2 — 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?
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Thảo luận & góp ý
Hỏi đáp về bài này