Mở console JavaScript trên trình duyệt và gõ 0.1 + 0.2 — thay vì 0.3, kết quả trả về là 0.30000000000000004. Đây không phải lỗi riêng của JavaScript — đó là hệ quả trực tiếp của cách chuẩn IEEE 754 biểu diễn số thập phân dưới dạng nhị phân, và kết quả tương tự cũng xuất hiện ở PHP, Java, Python cùng gần như mọi ngôn ngữ khác dựa trên chuẩn này.
Ba phần của một số dấu phẩy động
Một số theo IEEE 754 được lưu trữ dưới dạng ba thành phần: dấu (dương hoặc âm), phần định trị (các chữ số có nghĩa của số) và số mũ (lũy thừa mà dấu thập phân được dịch chuyển). Điều này tương tự với ký hiệu khoa học: 1.5 × 10², chỉ khác là dưới dạng nhị phân.
Vì sao 0.1 không thể biểu diễn chính xác dưới dạng nhị phân
0.1 trong hệ thập phân là một phân số vô hạn trong hệ nhị phân (tương tự như cách 1/3 là vô hạn trong hệ thập phân). Máy tính chỉ lưu trữ một số bit định trị giới hạn, nên số bị làm tròn đến giá trị gần nhất có thể biểu diễn được — và chính việc làm tròn đó gây ra "sai số" có thể nhìn thấy.
Nơi điều này thực sự gây rắc rối: tổng tiền giỏ hàng
Một trang thanh toán cộng dồn giá từng sản phẩm dưới dạng số thực đơn thuần có thể lệch một vài đồng so với tổng dự kiến sau đủ nhiều sản phẩm — mỗi phép cộng làm tròn một chút, và sai số tích lũy dần thay vì triệt tiêu lẫn nhau. Đó là lý do các hệ thống thanh toán thường tính bằng đơn vị tiền tệ nhỏ nhất dạng số nguyên, hoặc dùng kiểu decimal chuyên dụng, thay vì cộng trực tiếp các số thực.
Vì sao cần điều này
- Hiểu và chẩn đoán các lỗi làm tròn không mong muốn trong tính toán tài chính hoặc khoa học.
- Xem biểu diễn nhị phân chính xác của một số dấu phẩy động cụ thể.
- Giải thích cho đồng nghiệp hoặc sinh viên vì sao so sánh các số thập phân theo độ bằng chính xác là một thực hành tồi.
Số phi chuẩn gần số không
Khi giá trị của một số quá nhỏ để biểu diễn theo cách thông thường (phần định trị bắt đầu bằng số 1 ngầm định), IEEE 754 chuyển sang chế độ phi chuẩn hóa (subnormal), trong đó số 1 ngầm định này bị bỏ qua — điều này cho phép biểu diễn những số có độ lớn nhỏ hơn nữa, với cái giá là mất độ chính xác dần dần thay vì nhảy đột ngột về 0. Sự chuyển đổi "mượt mà" này về 0 tốt hơn là bị đưa về 0 đột ngột, nhưng các phép tính phi chuẩn chậm hơn rõ rệt so với phép tính thông thường trên một số bộ xử lý.