0

Công cụ Trực quan Số thực IEEE-754

Xem chính xác cách một số thập phân được lưu trữ dưới dạng số thực IEEE-754 — bit dấu, các bit số mũ và các bit phần định trị — và lý do những số như 0.1 không được lưu một cách chính xác.

🔒 Được xử lý hoàn toàn trong trình duyệt của bạn — không có nội dung nào bạn nhập ở đây được tải lên.

Đang xử lý... 0%
Đang chuẩn hóa
Đang tính độ lệch số mũ
Đang mở rộng các bit phần định trị
Hoàn tất

Kết quả

Mọi số dấu phẩy động mà máy tính lưu trữ — số trong JavaScript, float trong C, float trong Python — đều được mã hóa theo cùng một cách theo IEEE 754: một bit dấu, một khối bit số mũ và một khối bit phần định trị (phần thập phân). Độ chính xác đơn (32 bit) sử dụng 1 bit dấu, 8 bit số mũ và 23 bit phần định trị; độ chính xác kép (64 bit), định dạng ẩn sau kiểu số mặc định của hầu hết mọi ngôn ngữ, sử dụng 1 bit dấu, 11 bit số mũ và 52 bit phần định trị. Công cụ này lấy bất kỳ số thập phân nào bạn nhập, mã hóa nó ở một trong hai độ rộng bằng chính bộ mã hóa số thực DataView/ArrayBuffer của trình duyệt — cùng cơ chế mà mã của bạn thực sự chạy — và tô màu từng bit theo vùng mà nó thuộc về.

Quá trình mã hóa tuân theo ba bước và công cụ này sẽ đi qua tất cả chúng thay vì chỉ hiển thị các bit cuối cùng. Đầu tiên, chuẩn hóa: số được viết lại dưới dạng 1.phần_định_trị × 2^số_mũ, với đúng một chữ số khác không trước dấu nhị phân (những số quá nhỏ sẽ được xử lý theo dạng subnormal, không có số 1 ngầm định ở đầu). Thứ hai, độ lệch số mũ: vì trường số mũ phải lưu trữ cả số mũ dương và số mũ âm dưới dạng số không dấu, nên một độ lệch cố định (127 cho độ chính xác đơn, 1023 cho độ chính xác kép) được cộng vào trước khi trường này được ghi ra dưới dạng nhị phân. Thứ ba, phần định trị thập phân được mở rộng từng bit một bằng phương pháp cổ điển "nhân với hai, lấy phần nguyên làm bit tiếp theo, giữ lại phần dư" — lặp lại cho đến khi trường phần định trị đầy, lúc đó các bit còn lại sẽ quyết định kết quả được làm tròn lên hay xuống (round-half-to-even, giá trị mặc định của IEEE-754).

Đây chính là lý do một số bình thường như 0.1 không được lưu chính xác. Trong hệ nhị phân, 0.1 là một phân số lặp vô hạn (0.0001100110011…, kéo dài mãi) nên không có phần định trị hữu hạn nào có thể chứa chính xác nó — nó phải được làm tròn ở đâu đó. Hãy mã hóa 0.1 dưới dạng số thực 32 bit và đọc ngược lại các bit, bạn sẽ không nhận lại được 0.1: bạn nhận được chính xác 0.100000001490116119384765625. Độ chính xác kép có thêm 29 bit phần định trị để làm việc, nên sai số làm tròn của nó nhỏ hơn nhiều, nhưng cũng không bằng không — 0.1 dưới dạng double 64 bit chính xác là 0.1000000000000000055511151231257827021181583404541015625. Công cụ này tính toán giá trị được lưu chính xác đó bằng số học số nguyên lớn (không dùng đường tắt dấu phẩy động), vì vậy khoảng cách giữa những gì bạn nhập và những gì phần cứng thực sự giữ lại không bao giờ bị ẩn đi hay xấp xỉ.

Các giá trị đặc biệt có các mẫu bit dành riêng, chính xác thay vì mã hóa thông thường: số không và số không âm có các bit số mũ và bit phần định trị toàn 0 (chỉ khác nhau ở bit dấu), ±Vô cực có các bit số mũ toàn 1 với phần định trị toàn 0, và NaN có các bit số mũ toàn 1 với ít nhất một bit phần định trị được bật. Mọi thứ ở đây chạy cục bộ trong trình duyệt của bạn — không có gì bạn nhập được tải lên đâu cả — điều này khiến nơi đây trở thành một không gian an toàn để xây dựng trực giác thực sự về lỗi dấu phẩy động: tại sao `0.1 + 0.2 !== 0.3` trong hầu hết mọi ngôn ngữ lập trình, tại sao texture float32 trong mã đồ họa mất độ chính xác mà float64 thì không, và tại sao mã tài chính nói chung nên tránh hoàn toàn dấu phẩy động nhị phân.