0Pricing
Java Academy · บทเรียน

BigDecimal สำหรับการคำนวณทางการเงิน

ใช้ BigDecimal เพื่อหลีกเลี่ยงข้อผิดพลาดด้านความแม่นยำของเลขทศนิยมในการคำนวณเงินและภาษี

BigDecimal สำหรับการคำนวณทางการเงิน เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

BigDecimal สำหรับการคำนวณทางการเงิน

ชนิดข้อมูลเลขทศนิยมแบบลอยตัว (double, float) ไม่สามารถแทนค่าทศนิยมทั้งหมดได้อย่างแม่นยำ เนื่องจากใช้การแทนค่าแบบเลขฐานสอง สำหรับเงินและภาษี ให้ใช้ BigDecimal

ปัญหาของเลขทศนิยมแบบลอยตัว

เลขทศนิยมแบบลอยตัวฐานสองทำให้เกิดข้อผิดพลาดจากการปัดเศษเล็กน้อย ในแอปพลิเคชันทางการเงิน ข้อผิดพลาดเหล่านี้จะสะสมและก่อให้เกิดข้อผิดพลาดจริงได้

double price = 0.1 + 0.2;
System.out.println(price);          // 0.30000000000000004 (WRONG!)
System.out.println(price == 0.3);   // false

// Real bug: total never equals expected
double total = 0;
for (int i = 0; i < 10; i++) total += 0.1;
System.out.println(total); // 0.9999999999999999

การสร้าง BigDecimal อย่างปลอดภัย

ควรสร้าง BigDecimal จาก String เสมอ ไม่ใช่จาก double เพราะการใช้ตัวสร้างที่รับ double จะนำความคลาดเคลื่อนของเลขทศนิยมแบบลอยตัวติดมาด้วย

import java.math.BigDecimal;

// WRONG: double constructor
BigDecimal wrong = new BigDecimal(0.1);
System.out.println(wrong); // 0.1000000000000000055511...

// CORRECT: String constructor
BigDecimal correct = new BigDecimal("0.1");
System.out.println(correct); // 0.1

// Or use valueOf (also safe)
BigDecimal safe = BigDecimal.valueOf(0.1);
System.out.println(safe); // 0.1

การคำนวณพื้นฐานด้วย BigDecimal

BigDecimal ใช้การเรียกเมธอดสำหรับการคำนวณ ได้แก่ add(), subtract(), multiply(), divide() เมธอดเหล่านี้จะคืนอินสแตนซ์ BigDecimal ใหม่ (เป็นชนิดที่เปลี่ยนแปลงค่าไม่ได้)

import java.math.BigDecimal;

BigDecimal price = new BigDecimal("19.99");
BigDecimal tax   = new BigDecimal("0.08");
BigDecimal qty   = new BigDecimal("3");

BigDecimal subtotal = price.multiply(qty);
BigDecimal taxAmount = subtotal.multiply(tax);
BigDecimal total = subtotal.add(taxAmount);

System.out.println(subtotal);  // 59.97
System.out.println(taxAmount); // 4.7976
System.out.println(total);     // 64.7676

โหมดการปัดเศษ

RoundingMode ควบคุมวิธีที่ BigDecimal ใช้ปัดเศษ โหมดสำคัญมีดังนี้:

  • HALF_UP — การปัดเศษมาตรฐาน (0.5 จะปัดขึ้น)
  • HALF_EVEN — การปัดเศษแบบธนาคาร (0.5 จะปัดไปยังจำนวนคู่ที่ใกล้ที่สุด)
  • DOWN — ตัดเศษเข้าหาศูนย์
  • CEILING — ปัดเข้าหาอนันต์บวก
import java.math.*;

BigDecimal val = new BigDecimal("2.565");

System.out.println(val.setScale(2, RoundingMode.HALF_UP));   // 2.57
System.out.println(val.setScale(2, RoundingMode.HALF_EVEN)); // 2.56 (banker's)
System.out.println(val.setScale(2, RoundingMode.DOWN));      // 2.56
System.out.println(val.setScale(2, RoundingMode.CEILING));   // 2.57

การหารและสเกล

การหารด้วย BigDecimal จำเป็นต้องระบุสเกลและโหมดการปัดเศษ เพื่อหลีกเลี่ยง ArithmeticException เมื่อได้ทศนิยมที่ไม่สิ้นสุด

import java.math.*;

BigDecimal revenue = new BigDecimal("1000.00");
BigDecimal months  = new BigDecimal("3");

// Without scale → ArithmeticException (non-terminating decimal)
try {
    revenue.divide(months);
} catch (ArithmeticException e) {
    System.out.println("Need to specify scale!");
}

// Correct: specify scale and rounding
BigDecimal monthly = revenue.divide(months, 2, RoundingMode.HALF_UP);
System.out.println(monthly); // 333.33

การเปรียบเทียบ BigDecimal

ใช้ compareTo() เพื่อเปรียบเทียบค่า ไม่ใช่ equals() เพราะ equals() พิจารณาสเกลด้วย ดังนั้น 2.0 และ 2.00 จึงไม่เท่ากันเมื่อใช้ equals() แต่เท่ากันเมื่อใช้ compareTo()

import java.math.BigDecimal;

BigDecimal a = new BigDecimal("2.0");
BigDecimal b = new BigDecimal("2.00");

System.out.println(a.equals(b));        // false (different scale)
System.out.println(a.compareTo(b));     // 0 (same value)
System.out.println(a.compareTo(b) == 0); // true

// For sorting in collections
import java.util.*;
List<BigDecimal> prices = Arrays.asList(
    new BigDecimal("3.50"), new BigDecimal("1.00"), new BigDecimal("2.75")
);
Collections.sort(prices);
System.out.println(prices); // [1.00, 2.75, 3.50]

MathContext สำหรับเลขนัยสำคัญ

MathContext ระบุความแม่นยำ (เลขนัยสำคัญ) และโหมดการปัดเศษ ให้ใช้เมื่อจำเป็นต้องควบคุมความแม่นยำของเลขนัยสำคัญให้สอดคล้องกัน แทนการกำหนดจำนวนตำแหน่งทศนิยม

import java.math.*;

MathContext mc = new MathContext(4, RoundingMode.HALF_UP);

BigDecimal a = new BigDecimal("1234567");
BigDecimal b = new BigDecimal("3");

BigDecimal result = a.divide(b, mc);
System.out.println(result); // 4.115E+5 (4 significant digits)

// DECIMAL32, DECIMAL64, DECIMAL128 are standard MathContexts
BigDecimal precise = new BigDecimal("355").divide(
    new BigDecimal("113"), MathContext.DECIMAL64);
System.out.println(precise); // 3.141592920353982...

ตัวอย่างตะกร้าสินค้า

การคำนวณตะกร้าสินค้าอย่างครบถ้วนด้วย BigDecimal ซึ่งครอบคลุมรายการสินค้า ภาษี ส่วนลด และยอดรวมสุดท้าย

import java.math.*;

record LineItem(String name, BigDecimal price, int qty) {
    BigDecimal subtotal() {
        return price.multiply(new BigDecimal(qty));
    }
}

var items = List.of(
    new LineItem("Laptop",  new BigDecimal("999.00"), 1),
    new LineItem("Case",    new BigDecimal("29.99"),  2),
    new LineItem("Charger", new BigDecimal("49.99"),  1)
);

BigDecimal subtotal = items.stream()
    .map(LineItem::subtotal)
    .reduce(BigDecimal.ZERO, BigDecimal::add);

BigDecimal taxRate = new BigDecimal("0.09");
BigDecimal tax = subtotal.multiply(taxRate).setScale(2, RoundingMode.HALF_UP);
BigDecimal total = subtotal.add(tax);

System.out.println("Subtotal: " + subtotal);
System.out.println("Tax:      " + tax);
System.out.println("Total:    " + total);

Stripe: สกุลเงินในหน่วยเซนต์

ผู้ประมวลผลการชำระเงินอย่าง Stripe จัดเก็บจำนวนเงินเป็นจำนวนเซนต์แบบจำนวนเต็ม เพื่อหลีกเลี่ยงปัญหาทศนิยมโดยสิ้นเชิง การแปลงระหว่างจำนวนเซนต์กับจำนวนเงินที่แสดงเป็นรูปแบบที่ใช้กันทั่วไป

import java.math.*;

// Store as cents (long) for database / API
long amountCents = 999_99L; // $999.99

// Convert to BigDecimal for display
BigDecimal amount = BigDecimal
    .valueOf(amountCents)
    .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);

System.out.println("Amount: $" + amount); // Amount: $999.99

// Convert user input back to cents
BigDecimal input = new BigDecimal("49.95");
long cents = input.multiply(new BigDecimal("100"))
    .setScale(0, RoundingMode.HALF_UP).longValueExact();
System.out.println("Cents: " + cents); // Cents: 4995

ควรใช้ BigDecimal หรือ double เมื่อใด

เลือกชนิดข้อมูลให้เหมาะกับงาน:

  • BigDecimal: จำนวนเงิน ภาษี ใบแจ้งหนี้ และกรณีที่จำเป็นต้องแทนค่าทศนิยมอย่างแม่นยำ
  • double: การคำนวณทางวิทยาศาสตร์ กราฟิก และการเรียนรู้ของเครื่อง ซึ่งให้ความสำคัญกับความเร็วและยอมรับข้อผิดพลาดเล็กน้อยได้
  • int/long cents: ส่วนติดต่อการชำระเงินและการจัดเก็บจำนวนเงินในฐานข้อมูล

ตรวจสอบอย่างรวดเร็ว

ควรใช้เมธอดใดของ BigDecimal เพื่อตรวจสอบว่า BigDecimal สองค่ามีค่าเท่ากันในเชิงตัวเลข โดยไม่คำนึงถึงสเกล

ทบทวน: BigDecimal สำหรับการคำนวณทางการเงิน

ประเด็นสำคัญ:

  • double/float ทำให้เกิดข้อผิดพลาดจากการปัดเศษ — ห้ามใช้กับจำนวนเงิน
  • สร้าง BigDecimal จาก String หรือ BigDecimal.valueOf() ไม่ใช่จาก double
  • ใช้ add(), subtract(), multiply(), divide() สำหรับการคำนวณ
  • ระบุสเกลและ RoundingMode ทุกครั้งเมื่อทำการหาร
  • ใช้ compareTo() เพื่อตรวจสอบความเท่ากัน ไม่ใช่ equals()
  • พิจารณาจัดเก็บเงินเป็นจำนวนเซนต์แบบจำนวนเต็มเมื่อต้องทำงานกับฐานข้อมูลหรือส่วนติดต่อการเขียนโปรแกรม

คำถามที่พบบ่อย

บทเรียน “BigDecimal สำหรับการคำนวณทางการเงิน” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “BigDecimal สำหรับการคำนวณทางการเงิน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “BigDecimal สำหรับการคำนวณทางการเงิน”

ใช้ BigDecimal เพื่อหลีกเลี่ยงข้อผิดพลาดด้านความแม่นยำของเลขทศนิยมในการคำนวณเงินและภาษี คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “BigDecimal สำหรับการคำนวณทางการเงิน” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม

ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. พื้นฐานสำคัญของคลาส Math
  2. การคำนวณจำนวนเต็มและโอเวอร์โฟลว์
  3. BigDecimal สำหรับการคำนวณทางการเงิน
  4. NumberFormat และ printf
← กลับไปที่ Java Academy