Nếu bạn nghĩ máy tính luôn đúng, hãy thử câu lệnh sau
print( 0.1 + 0.2 + 0.3 == 0.6 )
Python sẽ trả về kết quả False?
Tại sao lại có sự tính toán sai lầm này của máy tính. Để trả lời câu hỏi, ta cần tìm hiểu về cách máy tính lưu trữ số thực (float number) trong bộ nhớ và tại sao lại có sự sai số như vậy.
Số lượng bit dùng để lưu 1 số là cố định với mọi máy tính,
thường là 64 bit. Các số được lưu trữ bởi 3 phần: phần xác định dấu s(sign indicator), để xác định
số đó là âm hay dương; phần số mũ e(exponent), là lũy thừa của 2 , phần hệ số
f(fraction), hệ số của luỹ thừa bậc 2. Ví dụ nếu phần s, e, f của một số là 1,
10, 11 thì số đó là (-1)^1 * 2^2 * 3 = -12, hoặc nếu s,e,f lần lượt là 0, 11,
101 thì số đó là (-1)^0 * 2^3 * 5 = 40
Trong Python, chúng ta có thể lấy thông tin float number bằng thư viện sys như bên dưới:
import sys
sys.float_info
Các số thập phân được lưu dưới dạng chung:
n = (-1)^s * 2^(e-1023) * (1+f)
Câu hỏi: Số 1 10000000010
1000000000000000000000000000000000000000000000000000 (IEEE754) là số nào trong
hệ cơ số 10
Ta có phần xác định dấu là 1, đại diện cho số dương; phần số
mũ là 10000000010, đại diện cho 1 * 2^10 + 1 * 2^1 = 1026, phần cơ số là 1 * 1/
2^1 + 0 * 1/ 2^2 + 0 * 1/ 2^3 + … = 0.5 Do đó từ dạng chung ta có
n = (-1)^1 * 2^3 * (1+0.5) = -12
Bằng việc lưu trữ số bằng chuẩn IEEE754, số lớn nhất máy
tính có thể lưu trữ được là
0 11111111110 1111111111111111111111111111111111111111111111111111 (IEEE754), khoảng 1.79 * 10^308 trong cơ số 10, và số dương nhỏ nhất có thể lưu được là 0 00000000001 000000000000000000000000000000000000000000000000000 (IEEE754), bằng khoảng 5 * 2^-324, và mọi số nhỏ hơn số này được máy tính coi là bằng 0.
Tuy nhiên chúng ta có thể thấy việc lưu trữ số bằng chuẩn
IEEE754 khiến máy tính không thể lưu trữ chính xác được giá trị của mọi số thực(do
trên lí thuyết thì số các số khác nhau trong chuẩn IEEE754 là rất lớn nhưng vẫn
hữu hạn), mà chỉ lưu một giá trị xấp xỉ của số đó. Để dễ hình dung hơn, ta sẽ
xét ví dụ sau
Trong chuẩn IEEE754 số 15 được lưu dưới dạng 0 10000000010 1110000000000000000000000000000000000000000000000000, số liền kề nhỏ hơn nó là 0 10000000010 1101111111111111111111111111111111111111111111111111 = 14.9999999999999982236431605997, và số liền kề lớn hơn nó là 0 10000000010 1110000000000000000000000000000000000000000000000001 = 15.0000000000000017763568394003
Do đó, với mỗi số thực nằm ở trong khoảng trên đều được biểu diễn bởi một trong ba số này trong chuẩn IEEE754, cũng có nghĩ khi tính toán, mọi kết quả nằm trong khoảng này đều được làm tròn về giá trị chuẩn IEEE754 gần nhất, chính điều này đã tạo ra sự sai lệch trong tính toán ở câu hỏi đưa ra ở phần đầu bài viết. Khi đó, trong khi tính toán 0.1 + 0.2 + 0.3 máy tính đã xấp xỉ kết quả này bằng một giá trị IEEE754 rất gần 0.6(nhưng không cùng giá trị IEEE754 với số 0.6), nên khi in ra kết quả của 0.1 + 0.2 + 0.3 == 0.6 sẽ là False.
