![Lỗi tính toán số thực float và câu chuyện khiến 28 quân nhân Mỹ thiệt mạng Lỗi tính toán số thực float và câu chuyện khiến 28 quân nhân Mỹ thiệt mạng](https://tuananhuet.com/wp-content/uploads/2022/10/IEEE-754-ENGLISH-370x147.jpg)
Lỗi tính toán số thực float và câu chuyện khiến 28 quân nhân Mỹ thiệt mạng
Trong buổi họp chia sẻ các vấn đề dự án, mình nhận được thông tin về một bug khá thú vị về lỗi tính toán số thực float hoặc double dẫn tới tính sai tiền trên hệ thống. Đây là một lỗi rất dễ gặp và nghiêm trọng khi làm việc với số float Cùng mình tìm hiểu chi tiết lỗi này nhé!
Vấn đề gặp phải
Mình lấy một ví dụ đơn giản về giá trị float như sau trong php:
$a = 53;
$b = 52.99;
echo $a - $b;
Bạn thử đoán xem kết quả sẽ in ra như thế nào ? Bạn đang nghĩ tới con số 0.01 ở trong đầu ? nhưng không kết quả in ra sẽ là 0.009999999999998. Mặc dù trông có vẻ là lỗi trong lập trình không nghiêm trọng lắm, nhưng trên thực tế thì nó lại rất nguy hiểm :
Cũng chính vì vấn đề này mà vào ngày 25/02/1991, Một tên lửa Scud đã bắn trúng căn cứ Dharan-Ả Rập Xê-út, khiến 28 quân nhân Mỹ thiệt mạng và gần 100 quân nhân bị thương mặc dù có khẩu đội tên lửa đánh chặn Patriot.
Nguyên nhân sau khi điều tra là do hệ thống đồng hồ của Patriot đã tính toán lệch ⅓ của 1s. Với các dòng tên lửa tốc độ nhanh như Scud thì Patriot đã bắn lệch mục tiêu hơn 500m. Rõ ràng radar đã phát hiện được Scud nhưng vì lỗi tính toán của đồng hồ đã gây nên hậu quả nghiêm trọng.
Xem thêm về báo cáo sự cố này tại đây!
Nguyên nhân
Như bạn đã biết thì dù bạn có sử dụng phép toán gì trên máy tính đi chăng nữa thì máy tính vẫn sẽ xử lý dưới dạng nhị phân các số 0 và 1. Vì vậy con số 1/100 (0.01) hay ⅓ = 0.333333333… như vấn đề ở trên không thể biểu diễn chính xác trong hệ nhị phân được mà nó gọi là floating-point (dấu phẩy động).
![Lỗi tính toán số thực float và câu chuyện khiến 28 quân nhân Mỹ thiệt mạng 1 IEEE-754-ENGLISH](https://tuananhuet.com/wp-content/uploads/2022/10/IEEE-754-ENGLISH-1024x408.jpg)
Giải pháp
Có rất nhiều giải pháp để giải quyết vấn đề này như dùng thư viện, tự viết hàm xử lý,… Trong bài viết này mình xin chia sẻ 1 số phương án mà mọi người thường dùng :
- Sử dụng number_format trong php
- VD: echo number_format(0.009999999999998, 2)
- Sử dụng extension Decimal extension php. Extension này cho phép làm tròn chính xác hoặc độ chính xác tùy ý số thập phân
- Sử dụng Decimal với My SQL
Ngoài ra trong javascript, bạn có thể dùng 1 số thư viện như: numeral.js, big.js, bigdecimal.js , ..
Kết luận
Mỗi khi làm việc với số thập phân thì cần chú ý vấn đề này. Việc không để ý sẽ có thể gây ra hậu quả nghiêm trọng như tính toán sai tiền thanh toán, tài chính,…