Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Java

การจัดการ Precision (ความแม่นยำ) ของตัวเลขทศนิยมใน Java

Java มีหลายวิธีจัดการความแม่นยำของ floating-point numbers ขึ้นอยู่กับ Use Case

ปัญหาของ float/double

public class FloatPrecisionIssue {
    public static void main(String[] args) {
        System.out.println(0.1 + 0.2); // 0.30000000000000004 ❌
        System.out.println(1.03 - 0.42); // 0.6100000000000001 ❌
        
        // เพราะ binary floating-point ไม่สามารถ represent บางทศนิยมได้แม่นยำ
    }
}

วิธีที่ 1: String.format() / printf - สำหรับ Display เท่านั้น

public class FormatExample {
    public static void main(String[] args) {
        double value = 34.909;
        
        // Format to 7 decimal places
        System.out.println(String.format("%.7f", value)); // 34.9090000
        
        // printf
        System.out.printf("Value: %.2f%n", 12.56); // 12.56
        
        // จัดรูปแบบต่างๆ
        System.out.printf("Scientific: %e%n", 1234.56);     // 1.234560e+03
        System.out.printf("General: %g%n", 1234.56);        // 1234.56
        System.out.printf("Hex float: %a%n", 12.56);        // 0x1.98f5c28f5c28fp3
    }
}

วิธีที่ 2: DecimalFormat - 靈活 Formatting

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;

public class DecimalFormatExample {
    public static void main(String[] args) {
        double value = 1234567.89123;
        
        // Pattern symbols: 0=digit, #=optional digit, .=decimal, ,=grouping
        DecimalFormat df1 = new DecimalFormat("#,###.00");
        System.out.println(df1.format(value)); // 1,234,567.89
        
        DecimalFormat df2 = new DecimalFormat("0.00000");
        System.out.println(df2.format(1.5));   // 1.50000
        
        // Custom symbols (Locale)
        DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US);
        symbols.setGroupingSeparator(',');
        symbols.setDecimalSeparator('.');
        DecimalFormat df3 = new DecimalFormat("#,##0.00", symbols);
        System.out.println(df3.format(value)); // 1,234,567.89
        
        // Currency
        DecimalFormat currency = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.US);
        System.out.println(currency.format(1234.56)); // $1,234.56
    }
}

วิธีที่ 3: BigDecimal - Exact Precision (แนะนำสำหรับ Financial/Monetary)

import java.math.BigDecimal;
import java.math.RoundingMode;

public class BigDecimalExample {
    public static void main(String[] args) {
        // ❌ ผิด: BigDecimal(double) เก็บค่า binary approximation
        BigDecimal bad = new BigDecimal(0.1);
        System.out.println("Bad: " + bad); // 0.1000000000000000055511151231257827021181583404541015625
        
        // ✅ ถูกต้อง: ใช้ String constructor หรือ valueOf
        BigDecimal good1 = new BigDecimal("0.1");
        BigDecimal good2 = BigDecimal.valueOf(0.1); // ใช้ double.toString() internal
        System.out.println("Good: " + good1); // 0.1
        
        // Arithmetic operations
        BigDecimal a = new BigDecimal("10.5");
        BigDecimal b = new BigDecimal("2.3");
        
        System.out.println("Add: " + a.add(b));                    // 12.8
        System.out.println("Subtract: " + a.subtract(b));          // 8.2
        System.out.println("Multiply: " + a.multiply(b));          // 24.15
        System.out.println("Divide: " + a.divide(b, 4, RoundingMode.HALF_UP)); // 4.5652
        
        // Scale & Rounding
        BigDecimal div = a.divide(b, 10, RoundingMode.HALF_UP); // 10 decimal places
        System.out.println("Divide (10dp): " + div);
        
        // setScale - ปรับ scale
        BigDecimal scaled = div.setScale(2, RoundingMode.HALF_UP);
        System.out.println("Scaled: " + scaled); // 4.57
        
        // Compare
        System.out.println("Compare: " + a.compareTo(b)); // 1 (a > b)
    }
}

Rounding Modes

Modeคำอธิบายตัวอย่าง (1.5 → )
HALF_UPปัดขึ้นถ้า ≥ 0.5 (มาตรฐาน)2
HALF_DOWNปัดขึ้นถ้า > 0.51
HALF_EVENBanker's rounding (ปัดไปคู่)2 (1.5→2, 2.5→2)
UPปัดขึ้นจากศูนย์2
DOWNปัดลงไปศูนย์1
CEILINGปัดขึ้นไป +∞2
FLOORปัดลงไป -∞1
UNNECESSARYต้องหารลงตัว ไม่งั้น ExceptionError

วิธีที่ 4: Math.round / StrictMath - Simple Rounding

public class MathRoundExample {
    public static void main(String[] args) {
        double d = 34.909;
        
        // Round to integer
        long rounded = Math.round(d); // 35
        
        // Round to n decimal places (manual)
        double rounded2 = Math.round(d * 100.0) / 100.0; // 34.91
        double rounded3 = Math.round(d * 1000.0) / 1000.0; // 34.909
        
        System.out.println(rounded2);
        System.out.println(rounded3);
    }
}

สรุป: เลือกวิธีไหนดี?

Use Caseแนะนำเหตุผล
Financial/Money calculationsBigDecimalExact precision, ไม่มี floating-point errors
Scientific/Engineering calculationsdouble + Math.round/formatPerformance, precision พอสำหรับ measurement
Display formatting (UI, Reports)String.format / DecimalFormatFlexible patterns, Locale support
Simple rounding to intMath.roundFast, simple
High precision mathBigDecimal / Apache Commons MathArbitrary precision

Best Practices

  • ❌ อย่าใช้ float/double สำหรับเงิน/การเงิน
  • ✅ ใช้ BigDecimal พร้อม String constructor
  • ✅ ระบุ RoundingMode ทุกครั้งที่ divide/setScale
  • ✅ ใช้ BigDecimal.valueOf(double) ถ้าต้องรับ double input
  • ✅ เก็บเงินเป็น **สตางค์/เซนต์ (Integer)** ใน Database