Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Java

Xử lý độ chính xác số thực (Precision Handling) trong Java

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ùng new BigDecimal(0.1) (sẽ mang theo lỗi double).
  • Chỉ định scale và RoundingMode cho 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 CaseRecommended
Hiển thị đơn giản, log, debugprintf / String.format
Định dạng phức tạp, locale, pattern tùy chỉnhDecimalFormat
Tiền tệ, tài chính, tính toán chính xácBigDecimal
Khoa học, engineering, hiệu năng caodouble + printf(%e)
Compact notation (1.2K, 3.5M)NumberFormat.getCompactNumberInstance (Java 17+)