Không. JS dùng chuẩn IEEE 754 double-precision (64-bit) cho mọi số. Nhiều số thập phân không biểu diễn chính xác được trong nhị phân → rounding errors.
Ví dụ kinh điển
<script>
document.write(0.1 + 0.2); // 0.30000000000000004
document.write('<br>');
document.write(0.3 + 0.6); // 0.8999999999999999
document.write('<br>');
document.write(0.2 + 0.1); // 0.30000000000000004
</script>
Tại sao?
Số thập phân như 0.1, 0.2, 0.3 là số vô hạn tuần hoàn trong nhị phân (giống 1/3 = 0.333... trong thập phân). Máy tính cắt bớt → sai số nhỏ.
Giải pháp phổ biến
1. Nhân chia hệ số 10^n (Integer math)
function addFloat(a, b, precision = 2) {
const factor = 10 ** precision;
return Math.round(a * factor + b * factor) / factor;
}
addFloat(0.1, 0.2); // 0.3
2. toFixed() + Number() — Cho hiển thị
Number((0.1 + 0.2).toFixed(2)); // 0.3
// Lưu ý: toFixed trả về STRING
3. Thư viện: decimal.js, big.js, bignumber.js
import Decimal from 'decimal.js';
new Decimal(0.1).plus(0.2).toNumber(); // 0.3 — chính xác tuyệt đối
4. Epsilon comparison (so sánh gần đúng)
function floatEquals(a, b, epsilon = Number.EPSILON) {
return Math.abs(a - b) < epsilon;
}
floatEquals(0.1 + 0.2, 0.3); // true
Khi nào cần quan tâm?
- Tính toán tài chính, tiền tệ — bắt buộc dùng integer (cents) hoặc decimal library.
- So sánh equality — dùng epsilon.
- Hiển thị UI — dùng
toFixed,Intl.NumberFormat. - Toán học khoa học — chấp nhận error nhỏ, dùng library nếu cần chính xác.
Number.EPSILON
Sai số nhỏ nhất giữa 1 và số tiếp theo > 1: ~2.22e-16. Dùng làm epsilon mặc định cho so sánh float.
Tóm tắt
| Vấn đề | Giải pháp |
|---|---|
| 0.1 + 0.2 !== 0.3 | Integer math (cents), decimal.js, toFixed() |
| So sánh float | Math.abs(a-b) < Number.EPSILON |
| Hiển thị tiền tệ | Intl.NumberFormat('vi-VN', {style:'currency', currency:'VND'}) |
| Tính toán khoa học lớn | big.js, decimal.js |