BigDecimal ใน Java

เผยแพร่ในกลุ่ม
สวัสดี! ในการบรรยายวันนี้ เราจะพูดถึงตัวเลขจำนวนมาก ไม่ เกี่ยวกับสิ่งที่ใหญ่จริงๆ ก่อนหน้านี้เราได้เห็นตารางช่วงค่าสำหรับประเภทข้อมูลดั้งเดิมมากกว่าหนึ่งครั้ง ดูเหมือนว่านี้:
ประเภทดั้งเดิม ขนาดในหน่วยความจำ ช่วงของค่า
ไบต์ 8 บิต -128 ถึง 127
สั้น 16 บิต ถึง -32768 ถึง 32767
ถ่าน 16 บิต ตั้งแต่ 0 ถึง 65536
ภายใน 32 บิต จาก -2147483648 ถึง 2147483647
ยาว 64 บิต จาก -9223372036854775808 ถึง 9223372036854775807
ลอย 32 บิต จาก (2 ถึงกำลัง -149) ถึง ((2-2 ถึงกำลัง -23)*2 ถึงกำลัง 127)
สองเท่า 64 บิต จาก (-2 ยกกำลัง 63) ถึง ((2 ยกกำลัง 63) - 1)
บูลีน 8 (เมื่อใช้ในอาร์เรย์), 32 (เมื่อใช้ในอาร์เรย์ที่ไม่ใช่) จริงหรือเท็จ
หากเรากำลังพูดถึงจำนวนเต็ม ประเภทข้อมูลที่กว้างขวางที่สุดก็คือแบบยาวและเมื่อเราพูดถึงตัวเลขทศนิยม จะเป็นdouble แต่จะเกิดอะไรขึ้นถ้าจำนวนที่เราต้องการมีขนาดใหญ่มากจนไม่สามารถใส่ได้ยาว ด้วยซ้ำ ? ช่วงของค่า Long ที่เป็นไปได้นั้นค่อนข้างใหญ่ แต่ก็ยังจำกัดอยู่ที่ขนาดที่แน่นอน - 64 บิต เราจะคิดอย่างไรหากตัวเลขขนาดใหญ่มากของเรามีน้ำหนัก 100 บิต โชคดีที่คุณไม่จำเป็นต้องประดิษฐ์อะไรขึ้นมา ใน Java มีการสร้างคลาสพิเศษสองคลาสสำหรับกรณีดังกล่าว - BigInteger (สำหรับจำนวนเต็ม) และBigDecimal (สำหรับตัวเลขทศนิยม) คุณสมบัติของพวกเขาคืออะไร? ประการแรก พวกมันไม่มีขนาดสูงสุดตามทฤษฎี ตามทฤษฎีแล้ว เนื่องจากไม่มีคอมพิวเตอร์ที่มีหน่วยความจำไม่จำกัด และถ้าคุณสร้างตัวเลขในโปรแกรมที่มีขนาดใหญ่กว่าขนาดหน่วยความจำของคอมพิวเตอร์ แน่นอนว่า โปรแกรมจะไม่ทำงาน แต่กรณีดังกล่าวไม่น่าเป็นไปได้ ดังนั้นเราจึงสามารถพูดได้ว่าขนาดของตัวเลขนั้นBigIntegerแทบBigDecimalจะไร้ขีดจำกัด คลาสเหล่านี้ใช้ทำอะไร? ประการแรก สำหรับการคำนวณที่ต้องการความแม่นยำสูงมาก ตัวอย่างเช่น มีโปรแกรมที่ชีวิตมนุษย์สามารถขึ้นอยู่กับความแม่นยำของการคำนวณ (ซอฟต์แวร์สำหรับเครื่องบินและจรวดหรือสำหรับอุปกรณ์ทางการแพทย์) ดังนั้นหากแม้ทศนิยมตำแหน่งที่ 150 ก็มีบทบาทสำคัญBigDecimalก็ถือเป็นตัวเลือกที่ดีที่สุด นอกจากนี้บ่อยครั้งที่วัตถุเหล่านี้ถูกใช้ในโลกการเงินซึ่งความแม่นยำในการคำนวณจนถึงค่าที่เล็กที่สุดก็มีความสำคัญอย่างยิ่งเช่นกัน วิธีทำงานกับวัตถุBigIntegerและBigDecimalสิ่งสำคัญที่ต้องจำเกี่ยวกับวัตถุเหล่านั้น ออบเจ็กต์ของคลาสเหล่านี้ถูกสร้างขึ้นดังนี้:
public class Main {

   public static void main(String[] args) {

       BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
       System.out.println(integer);

       BigDecimal decimal = new BigDecimal("123.444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444");
       System.out.println(decimal);
   }
}
การส่งสตริงเป็นพารามิเตอร์เป็นเพียงหนึ่งในตัวสร้างที่เป็นไปได้ ในที่นี้เราใช้สตริงเพราะตัวเลขของเราเกินค่าสูงสุดlongและdoubleและอย่างใดเราต้องอธิบายให้คอมไพเลอร์ฟังว่าเราต้องการได้ตัวเลขอะไร :) เพียงแค่ส่งหมายเลข 1111111111111111111111111111111111111111111111111111111 ถึงผู้สร้าง 11111111111111111111111111111111111111111111111111111111111111111111 จะไม่ทำงาน: Java จะพยายาม “พอดี” ตัวเลขที่ส่งผ่านเป็นประเภทข้อมูลพื้นฐานประเภทใดประเภทหนึ่ง แต่จะไม่พอดีกับประเภทใดประเภทหนึ่ง ดังนั้นการใช้สตริงเพื่อส่งผ่านหมายเลขที่ต้องการจึงเป็นทางเลือกที่ดี ทั้งสองคลาสสามารถดึงค่าตัวเลขจากสตริงที่ส่งผ่านได้โดยอัตโนมัติ จุดสำคัญอีกจุดที่ต้องจำเมื่อทำงานกับคลาสจำนวนมากคืออ็อบเจ็กต์ของคลาสนั้นไม่เปลี่ยนรูป ( Immutable) คุณคุ้นเคยกับหลักการของความไม่เปลี่ยนรูปเป็นอย่างดีแล้วผ่านตัวอย่างของคลาสStringและคลาส wrapper สำหรับพื้นฐาน (จำนวนเต็ม ยาว และอื่นๆ)
import java.math.BigInteger;

public class Main {

   public static void main(String[] args) {

       BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
       System.out.println(integer);

       integer.add(BigInteger.valueOf(33333333));
       System.out.println(integer);

   }
}
เอาต์พุตคอนโซล:

1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
ตัวเลขของเราไม่เปลี่ยนแปลงอย่างที่คุณคาดหวัง เพื่อให้การดำเนินการเพิ่มสำเร็จ คุณต้องสร้างออบเจ็กต์ใหม่และกำหนดผลลัพธ์ของการเพิ่มให้กับออบเจ็กต์นั้น
import java.math.BigInteger;

public class Main {

   public static void main(String[] args) {

       BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
       System.out.println(integer);

       BigInteger result = integer.add(BigInteger.valueOf(33333333));
       System.out.println(result);

   }
}
เอาต์พุตคอนโซล:

1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111144444444
ตอนนี้ทุกอย่างทำงานได้ตามที่ควรจะเป็น :) อย่างไรก็ตาม คุณสังเกตไหมว่าการดำเนินการเพิ่มเติมดูผิดปกติอย่างไร
BigInteger result = integer.add(BigInteger.valueOf(33333333));
นี่เป็นอีกจุดสำคัญ คลาสจำนวนมากไม่ได้ใช้ตัวดำเนินการ +-*/ ในการดำเนินการ แต่จะจัดเตรียมชุดของวิธีการแทน มาดูหลักๆ กัน (คุณสามารถค้นหารายการวิธีการทั้งหมดในเอกสารประกอบของ Oracle เช่นเคย: ที่นี่และที่นี่ )
  1. วิธีการดำเนินการทางคณิตศาสตร์: add() , subtract(), multiply(), divide(). ใช้สำหรับการบวก ลบ คูณ หาร ตามลำดับ

  2. doubleValue(), intValue(), floatValue(), longValue()ฯลฯ - ใช้เพื่อแปลงจำนวนมากให้เป็นประเภทดั้งเดิมของ Java ระวังเมื่อใช้และจำความแตกต่างในความจุ!

    import java.math.BigInteger;
    
    public class Main {
    
       public static void main(String[] args) {
    
           BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
    
           long result = integer.longValue();
           System.out.println(result);
    
       }
    }

    เอาต์พุตคอนโซล:

    
    8198552921648689607
  3. min()และmax()- ช่วยให้คุณค้นหาค่าต่ำสุดและสูงสุดของตัวเลขขนาดใหญ่สองตัวที่ผ่านไป
    โปรดทราบ: วิธีการไม่คงที่!

    import java.math.BigInteger;
    
    public class Main {
    
       public static void main(String[] args) {
    
           BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
           BigInteger integer2 = new BigInteger("222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222");
    
           System.out.println(integer.max(integer2));
    
       }
    }

    เอาต์พุตคอนโซล:

    
    222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222

การควบคุมการปัดเศษ BigDecimal

หัวข้อนี้รวมอยู่ในส่วนแยกต่างหากเนื่องจากการปัดเศษตัวเลขจำนวนมากและการปรับเปลี่ยนไม่ใช่เรื่องง่าย คุณสามารถกำหนดจำนวนตำแหน่งทศนิยมสำหรับตัวเลขได้BigDecimalโดยใช้ setScale()ตัวอย่างเช่น เราต้องการตั้งค่าความแม่นยำของตัวเลข111.5555555555เป็นทศนิยมสามตำแหน่ง อย่างไรก็ตาม เราจะไม่สามารถส่งผ่านหมายเลข 3 เป็นอาร์กิวเมนต์ของวิธีการsetScale()ได้ จึงช่วยแก้ปัญหาของเราได้ ตามที่กล่าวไว้ข้างต้นBigDecimalตัวเลขเหล่านี้เป็นตัวเลขสำหรับการคำนวณที่มีความแม่นยำเพิ่มขึ้น ในรูปแบบปัจจุบัน จำนวนของเรามีทศนิยม 10 ตำแหน่ง เราต้องการทิ้ง 7 ตัวและเหลือเพียง 3 ตัวเท่านั้น ดังนั้นนอกเหนือจากหมายเลข 3 แล้ว เราต้องผ่านโหมดการปัดเศษเป็นพารามิเตอร์ . มีโหมดการปัดเศษ ทั้งหมด 8 BigDecimalโหมด ค่อนข้างมาก! แต่ถ้าคุณต้องการปรับแต่งความแม่นยำของการคำนวณในโปรแกรมอย่างละเอียด คุณจะมีทุกสิ่งที่คุณต้องการสำหรับสิ่งนี้ ดังนั้นนี่คือโหมดการปัดเศษ 8 โหมดที่มีในBigDecimal:
  1. ROUND_CEILING- ปัดเศษขึ้น

    111.5555555555 -> setScale(3, ROUND_CEILING) -> 111.556
  2. ROUND_DOWN- การทิ้งทิ้ง

    111.5555555555 -> setScale(3, ROUND_DOWN) -> 111.555
  3. ROUND_FLOOR- ปัดเศษลง

    111.5555555555 -> setScale(3, ROUND_FLOOR) -> 111.555

  4. ROUND_HALF_UP— ปัดเศษขึ้นถ้าเป็นตัวเลขหลังจุดทศนิยม >= .5

    0.55 -> setScale(1, ROUND_HALF_UP) -> 0.6
    0.54 -> setScale(1, ROUND_HALF_UP) -> 0.5
  5. ROUND_HALF_DOWN— ปัดเศษขึ้นหากตัวเลขหลังจุดทศนิยม > .5

    0.55 -> setScale(1, ROUND_HALF_DOWN) -> 0.5
    0.56 -> setScale(1, ROUND_HALF_DOWN) -> 0.6
  6. ROUND_HALF_EVEN— การปัดเศษจะขึ้นอยู่กับตัวเลขทางด้านซ้ายของจุดทศนิยม หากตัวเลขทางซ้ายเป็นเลขคู่ การปัดเศษจะปัดลง หากตัวเลขทางด้านซ้ายของจุดทศนิยมเป็นเลขคี่ก็จะปัดเศษขึ้น

    2.5 -> setScale(0, ROUND_HALF_EVEN) -> 2

    ตัวเลขทางด้านซ้ายของจุดทศนิยม - 2 - เป็นเลขคู่ การปัดเศษเกิดขึ้น เนื่องจากเราต้องการทศนิยม 0 ตำแหน่ง ผลลัพธ์จะเป็น 2

    3.5 -> setScale(0, ROUND_HALF_EVEN) -> 4

    ตัวเลขทางด้านซ้ายของจุดทศนิยม - 3 - เป็นเลขคี่ การปัดเศษเกิดขึ้นด้านบน เนื่องจากเราต้องการทศนิยม 0 ตำแหน่ง ผลลัพธ์จะเป็น 4

  7. ROUND_UNNECCESSARY— ใช้ในกรณีที่จำเป็นต้องส่งโหมดการปัดเศษไปยังวิธีใดวิธีหนึ่ง แต่ไม่จำเป็นต้องปัดเศษตัวเลข ถ้าคุณพยายามที่จะปัดเศษตัวเลขเมื่อตั้งค่าโหมด ROUND_UNNECCESSARY ไว้ ArithmeticException จะถูกส่งออกไป

    3.51 -> setScale(1, ROUND_UNNECCESSARY) -> ArithmeticException
  8. ROUND_UP- ปัดเศษขึ้น

    111.5551 -> setScale(3, ROUND_UP) -> 111.556

การเปรียบเทียบตัวเลขจำนวนมาก

คำถามนี้ก็มีความสำคัญเช่นกัน คุณจำได้แล้วว่าวิธีนี้ใช้ในการเปรียบเทียบวัตถุในequals()Java มันถูกจัดเตรียมโดยภาษาเอง (ในกรณีของคลาสในตัวของ Java) หรือถูกแทนที่โดยโปรแกรมเมอร์ แต่ในกรณีของคลาสอ็อบเจ็กต์ ไม่แนะนำ BigDecimalให้ใช้วิธีequals()เปรียบเทียบ เหตุผลก็คือว่าBigDecimal.equals()วิธีตัวเลขสองตัวคืนค่าจริงก็ต่อเมื่อตัวเลขสองตัวมีค่าและมาตราส่วนเท่ากัน : ลองเปรียบเทียบพฤติกรรมของ วิธีequals()y Doubleและ y BigDecimal:
import java.math.BigDecimal;

public class Main {

   public static void main(String[] args) {

       Double a = 1.5;
       Double b = 1.50;

       System.out.println(a.equals(b));

       BigDecimal x = new BigDecimal("1.5");
       BigDecimal y = new BigDecimal("1.50");

       System.out.println(x.equals(y));

   }
}
เอาต์พุตคอนโซล:

true
false
อย่างที่คุณเห็นตัวเลข 1.5 และ 1.50 ในกรณีของ c BigDecimalกลับกลายเป็นว่าไม่เท่ากัน! สิ่งนี้เกิดขึ้นอย่างแม่นยำเนื่องจากลักษณะเฉพาะของวิธีการในequals()ชั้นเรียน BigDecimalเพื่อให้การเปรียบเทียบทั้งสองถูกต้องยิ่งขึ้นBigDecimalควรใช้วิธีนี้ดีกว่าcompareTo():
import java.math.BigDecimal;

public class Main {

   public static void main(String[] args) {

       BigDecimal x = new BigDecimal("1.5");
       BigDecimal y = new BigDecimal("1.50");

       System.out.println(x.compareTo(y));

   }
}
เอาต์พุตคอนโซล:

0
วิธีการcompareTo()ส่งคืน 0 ซึ่งหมายถึงเท่ากับ 1.5 และ 1.50 นี่คือผลลัพธ์ที่เราคาดหวัง! :) นี่เป็นการสรุปบทเรียนของเราสำหรับวันนี้ ได้เวลากลับไปทำหน้าที่แล้ว! :)
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION