Tại sao 0.1 + 0.2 !== 0.3 trong máy tính? Bản chất chuẩn số thực dấu phẩy động IEEE 754
Phép toán cơ bản nhất khiến máy tính bối rối Mở bất kỳ ngôn ngữ lập trình nào JavaScript, Python, C++, Go, Java và chạy biểu thức sau: Đây không phải là lỗi của riêng JavaScript, mà là đặc tính cố hữ…
☰ Mục lục bài viết (6 phần) Mở / Đóng mục lục
Phép toán cơ bản nhất khiến máy tính bối rối#
Mở bất kỳ ngôn ngữ lập trình nào (JavaScript, Python, C++, Go, Java) và chạy biểu thức sau:
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
Đây không phải là lỗi của riêng JavaScript, mà là đặc tính cố hữu của việc biểu diễn số thực trên máy tính theo Chuẩn quốc tế IEEE 754 (Định dạng dấu phẩy động 64-bit - Double Precision).
Nguyên nhân toán học: Hệ thập phân vs Hệ nhị phân#
Trong hệ thập phân (hệ cơ số 10), chúng ta không thể biểu diễn chính xác phân số $\frac{1}{3}$: $$\frac{1}{3} = 0.333333333... \text{ (vô hạn tuần hoàn)}$$
Tương tự như vậy trong máy tính (hệ cơ số 2 nhị phân):
- Một phân số chỉ có thể biểu diễn hữu hạn nếu mẫu số là lũy thừa của 2 (như $\frac{1}{2} = 0.5$, $\frac{1}{4} = 0.25$, $\frac{1}{8} = 0.125$).
- Số $0.1$ (tức $\frac{1}{10}$) và $0.2$ (tức $\frac{1}{5}$) khi chuyển sang hệ nhị phân sẽ trở thành số vô hạn tuần hoàn: $$0.1_{10} = 0.000110011001100110011..._2$$
- Vì thanh ghi 64-bit chỉ có đúng 52 bits cho phần định trị (Mantissa), máy tính bắt buộc phải làm tròn (Rounding Error). Sự sai lệch siêu nhỏ ở bit thứ 53 khi cộng lại tạo ra con số đuôi
...0004!
Giải pháp xử lý số liệu tài chính an toàn#
Nếu bạn làm phần mềm ngân hàng, thương mại điện tử, tuyệt đối không bao giờ dùng kiểu float hay double để tính tiền:
1. Quy đổi toàn bộ về số nguyên nhỏ nhất (Cents / Xu / Đồng)#
Thay vì lưu $10.50$, hãy lưu là 1050 xu (Integers). Mọi phép cộng trừ số nguyên luôn chính xác 100%.
2. Sử dụng thư viện số học độ chính xác tùy biến (Arbitrary-precision)#
- JavaScript: Sử dụng thư viện
decimal.jshoặcbignumber.js. - Java: Sử dụng
BigDecimal. - PostgreSQL: Sử dụng kiểu dữ liệu
NUMERIChoặcDECIMAL.
Góc thảo luận#
Dự án của bạn đang lưu trữ số dư tài khoản và giá sản phẩm bằng kiểu dữ liệu gì trong Database? Đã từng gặp lỗi lệch 1 đồng do làm tròn số chưa?
Xem thêm nội dung liên quan:
Thảo luận
1 phản hồi
Tham gia thảo luận
Bài liên quan trong #Backend & Microservices
-
Áp dụng Model Context Protocol (MCP) của Anthropic để kết nối AI với Postgres và Docker
Hoàng Nam (Tech Lead) · 💬 1 -
So sánh mô hình Embedding đa ngữ: OpenAI text-embedding-3 vs BGE-M3 vs Cohere
Minh Trí (AI Engineer) · 💬 1 -
Xây dựng Voice AI Agent thời gian thực với WebRTC và Whisper Live: Độ trễ dưới 500ms
Lan Anh (Senior Frontend) · 💬 1 -
Cấu hình Rate Limiting ở tầng Nginx và API Gateway: Chống DDOS Layer 7 hiệu quả
Đức Huy (DevOps Specialist) · 💬 1 -
Tokenization tiếng Việt của các LLM: Tại sao chi phí tiếng Việt đắt gấp 3 lần tiếng Anh?
Minh Trí (AI Engineer) · 💬 1