Java cung cấp nhiều cách để định dạng và kiểm soát độ chính xác của số thực (float, double). Lựa chọn phụ thuộc vào yêu cầu: hiển thị, tính toán tài chính, hoặc khoa học.
1. String.format() / System.out.printf() (Cách đơn giản, cho hiển thị)
double val = 34.909;
System.out.printf("%.7f%n", val); // 34.9090000 (7 chữ số sau dấu phẩy)
System.out.println(String.format("%.9f", 12.56)); // 12.560000000
%f: fixed-point, mặc định 6 decimal places.%.Nf: N chữ số sau dấu phẩy (làm tròn).%e / %E: Ký hiệu khoa học.%g / %G: Tự chọn %f hoặc %e tùy magnitude.
2. DecimalFormat (Linh hoạt hơn, pattern-based)
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
double val = 12345.6789;
DecimalFormat df1 = new DecimalFormat("#,###.00"); // 12,345.68
DecimalFormat df2 = new DecimalFormat("0.0000"); // 12345.6789
DecimalFormat df3 = new DecimalFormat("#.##%"); // 1234567.89% (nhân 100)
DecimalFormat df4 = new DecimalFormat("¥#,##0.00", new DecimalFormatSymbols(Locale.JAPAN)); // ¥12,345.68
System.out.println(df1.format(val));
System.out.println(df2.format(val));
System.out.println(df3.format(val * 0.01)); // 123.46%
System.out.println(df4.format(val));
3. BigDecimal (Chuẩn cho tài chính, tính toán chính xác)
Không bao giờ dùng float/double cho tiền tệ do lỗi biểu diễn binary floating-point (0.1 + 0.2 ≠ 0.3).
import java.math.BigDecimal;
import java.math.RoundingMode;
BigDecimal price = new BigDecimal("19.99"); // Luôn dùng STRING constructor
BigDecimal qty = new BigDecimal("3");
BigDecimal taxRate = new BigDecimal("0.08");
BigDecimal subtotal = price.multiply(qty); // 59.97
BigDecimal tax = subtotal.multiply(taxRate) // 4.7976
.setScale(2, RoundingMode.HALF_UP); // 4.80 (làm tròn 2 chữ số)
BigDecimal total = subtotal.add(tax); // 64.77
System.out.println("Total: " + total); // 64.77
// So sánh
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
System.out.println(a.add(b)); // 0.3 (CHÍNH XÁC)
BigDecimal Best Practices
- Luôn tạo từ
String:new BigDecimal("0.1"). KHÔNG dùngnew BigDecimal(0.1)(sẽ mang theo lỗi double). - Chỉ định
scalevàRoundingModecho phép chia:divide(divisor, scale, RoundingMode.HALF_UP). RoundingMode: HALF_UP (toán học), HALF_EVEN (banker's), DOWN, UP, CEILING, FLOOR.- Performance: chậm hơn double ~100-1000x. Chỉ dùng cho domain tài chính.
4. MathContext (Độ chính xác toàn cục)
MathContext mc = new MathContext(4, RoundingMode.HALF_UP); // 4 chữ số hiệu dụng
BigDecimal calc = new BigDecimal("1.23456789", mc);
System.out.println(calc); // 1.235
5. Java 17+: NumberFormat Compact Style
NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT); fmt.setMaximumFractionDigits(1); System.out.println(fmt.format(1234567)); // 1.2M
Tóm tắt lựa chọn
| Use Case | Recommended |
|---|---|
| Hiển thị đơn giản, log, debug | printf / String.format |
| Định dạng phức tạp, locale, pattern tùy chỉnh | DecimalFormat |
| Tiền tệ, tài chính, tính toán chính xác | BigDecimal |
| Khoa học, engineering, hiệu năng cao | double + printf(%e) |
| Compact notation (1.2K, 3.5M) | NumberFormat.getCompactNumberInstance (Java 17+) |