Back to Home

Tại sao 0.1+0.2 ≠ 0.3!?

Trong khi lập trình, bạn sẽ rất hay gặp phải một mệnh đề toán học rất vô lý: “0.1+0.2 ≠ 0.3“. Cho dù là bạn dùng C++, javascript, hay python … Mình đã từng đặt câu hỏi này trong các cuộc phỏng vấn, và câu hỏi này dường như rất ít ứng viên trả lời được.

Nếu không tin, bạn có thể kiểm chứng bằng cách mở browser ra, bấm F12, rồi nhập vào thử, bạn sẽ thấy như hình dưới:

Đây là một vấn đề nổi tiếng trong tính toán số học về dấu chấm động. Lỗi này xảy ra vì số thực (floating-point numbers) không thể được biểu diễn chính xác trong hệ nhị phân.

Con người chúng ta dùng hệ thập phân để biểu diễn một số bất kỳ bằng tổng một chuỗi các lũy thừa của 1010 . Ví dụ:

758.44=7×102+5×101+8×100+4×101+4×102758.44 = 7\times 10^2+5\times10^1+8\times10^0 + 4\times 10^{-1}+4\times10^{-2}

Khi chuyển sang hệ nhị phân, một số cũng được biểu diễn bằng tổng một chuỗi các lũy thừa của 22:

910=10012=1×23+0×22+0×21+1×201.510=1.12=1×20+1×216.7510=110.112=1×22+1×21+0×20+1×21+1×22\begin{align*} 9_{10} &= 1001_2 = 1\times 2^3 + 0\times 2^2+0 \times 2^1+1\times 2^0 \\ 1.5_{10} &= 1.1_{2} = 1\times2^0 + 1\times2^{-1}\\ 6.75_{10} &= 110.11_{2} = 1\times2^2+1\times2^1 + 0\times2^0 + 1\times2^{-1} +1\times2^{-2} \end{align*}

Các bạn chú ý là trong bài viết này, mình dùng ký hiệu dạng XbX_b để biểu đạt một giá trị XX ở hệ đếm bb. Ví dụ: 100121001_2 nghĩa là giá trị 10011001 ở hệ nhị phân và có giá trị là 99 ở hệ thập phân.

Trong hệ nhị phân, giá trị 0.10.1 thực chất là một chuỗi xấp xỉ vô hạn trong hệ nhị phân:

0.110=0.00011001100110011...20.1_{10}=0.00011001100110011..._2

Tương tự, 0.20.2 trong hệ thập phân cũng cũng là một dãy vô hạn xấp xỉ trong hệ nhị phân. Tuy nhiên, trong máy tính, dãy này không vô hạn mà bị giới hạn (thường là 3232 hoặc 6464 bit). Biểu diễn số ở máy tính được dùng theo chuẩn IEEE 754 sử dụng 64 bit, theo đó:

  • 0.1100011110111001100110011001100110120.1_{10} \to 00111101110011001100110011001101_2 (0x3DCCCCCD)

  • 0.2100011111001001100110011001100110120.2_{10} \to 00111110010011001100110011001101_2 (0x3E4CCCCD)

  • 0.3100011111010011001100110011001101020.3_{10} \to 00111110100110011001100110011010_2 (0x3E99999A)

Khi thực hiện phép tính 0.1+0.20.1 + 0.2 trên máy tính thì:

0.110+0.210=0011111010011001100110011001101120.1_{10} + 0.2_{10} = 00111110100110011001100110011011_2

Nhưng vậy giá trị của 0.1+0.20.1+0.20x3E99999B (khi biểu diễn bit) hay 0.30000004100.30000004_{10}. Vì thế kết quả này khi so sánh với 0.3100.3_{10} (0x3E99999A) ở trên là không khớp với (0x3E99999B) nên máy trả về False.

Đó chính là nguyên nhân sâu xa của nghịch lý này.

Comments

0/300

Leave name/email blank to comment anonymously

No comments yet. Be the first to comment!