Con Số Kỳ Diệu Để Chuyển Đổi Từ Double Sang Int - nói dối e blog

Con Số Kỳ Diệu Để Chuyển Đổi Từ Double Sang Int

Cách đây không lâu tôi đã viết một bài blog về tối ưu hóa hàm _ftol. Hôm nay, khi đang nghiên cứu mã nguồn Lua 5.1, tôi tình cờ phát hiện một mẹo cực kỳ thông minh và thú vị. Hóa ra việc chuyển đổi từ kiểu double sang kiểu int có thể đơn giản đến không ngờ qua đoạn mã sau:

1
2
3
union luai_Cast { double l_d; long l_l; };
#define lua_number2int(i,d) { volatile union luai_Cast u; \
  u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; }

Điều kỳ diệu của macro này nằm ở chỗ nó hoạt động chính xác cho cả số dương và số âm dưới dạng số double, thực hiện phép chuyển đổi sang số nguyên 32-bit theo cơ chế làm tròn tự nhiên. Con số 6755399441055744.0 thực chất là 1.5 × 2⁵². Khi cộng một số nguyên có giá trị tuyệt đối nhỏ hơn 2³¹ với con số “thần kỳ” này, theo quy tắc cộng số dấu phẩy động (theo dạng biểu diễn khoa học), phần mũ lớn hơn sẽ được ưu tiên căn chỉnh.

Đặc biệt, số 1.5 trong hệ nhị phân được biểu diễn là 1.1, và theo tiêu chuẩn IEEE 754, bit ẩn trước dấu phẩy (luôn là 1) không cần lưu trữ. Điều này khiến 4 byte đầu tiên của cấu trúc double trở nên “trống”, và kết quả phép cộng sẽ tự động được đặt đúng vào vị trí của phần số nguyên cần chuyển đổi.

Tuy nhiên, thủ thuật này không phải lúc nào cũng khả dụng. Ví dụ, sau khi khởi tạo Direct3D 9, phương pháp này sẽ không còn hiệu lực do D3D mặc định thay đổi độ chính xác của phép tính dấu phẩy động. Trong chế độ độ chính xác thấp, việc làm tròn số sẽ không thể thực hiện chính xác. Bạn có thể tham khảo thêm chi tiết về vấn đề này trong một bài đăng trên MSDN.

Ngoài ra, cần lưu ý rằng kỹ thuật này chỉ hoạt động đáng tin cậy khi hệ thống tuân thủ nghiêm ngặt tiêu chuẩn IEEE 754 về biểu diễn số dấu phẩy động. Trong một số môi trường biên dịch đặc biệt hoặc phần cứng không hỗ trợ đầy đủ tiêu chuẩn này, kết quả có thể không như mong đợi. Đây là một minh chứng đẹp mắt cho việc ứng dụng sâu sắc các đặc tính của định dạng số học vào lập trình hệ thống.

0%