CodeGym /Java Blog /Toto sisi /Java 中的大數
John Squirrels
等級 41
San Francisco

Java 中的大數

在 Toto sisi 群組發布
你好!在今天的課程中,我們將討論大數。不,我的意思是真的很大。我們以前多次遇到原始數據類型的值範圍表。它看起來像這樣:
原始類型 內存大小 取值範圍
字節 8位 -128 到 127
短的 16位 -32768 至 32767
字符 16位 0 到 65536
整數 32位 -2147483648 至 2147483647
長的 64位 -9223372036854775808 至 9223372036854775807
漂浮 32位 (2 的 -149 次方) 到 ((2 的 -23 次方) * 2 的 127 次方)
雙倍的 64位 (-2 的 63 次方) 到 ((2 的 63 次方) - 1)
布爾值 8(用於數組時),32(不用於數組時) 對或錯
最寬敞的整數數據類型是long。說到浮點數,那就是double但是,如果我們需要的數字大到連long都裝不下怎麼辦?Long 數據類型具有相當大的可能值範圍,但仍限於 64 位。如果我們的超大數需要 100 位,我們需要想出什麼?幸運的是,我們不需要發明任何東西。對於這種情況,Java 有兩個特殊類:BigInteger(用於整數)和BigDecimal(對於浮點數)。是什麼讓他們與眾不同?首先,理論上,它們沒有最大尺寸。我們說“理論上”,因為沒有無限內存的計算機。如果您的程序創建的數字大於可用內存量,那麼該程序當然將無法運行。但這種情況不太可能發生。因此,我們可以說BigIntegerBigDecimal可以表示幾乎無限大小的數字。這些類有什麼用?首先,對於精度要求極其嚴格的計算。例如,人的生命可能取決於某些程序(例如控制飛機、火箭或醫療設備的軟件)中計算的準確性。所以如果第 150 位小數很重要,那麼BigDecimal是最好的選擇。此外,此類對象經常用於金融領域,即使是最小值的準確計算也非常重要。您如何使用BigIntegerBigDecimal對象,您是否需要了解它們?這些類的對像是這樣創建的:

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);
   }
}
將字符串傳遞給構造函數只是一種可能的選擇。這裡我們使用字符串,因為我們的數字超過了longdouble的最大值,我們確實需要一些方法來向編譯器解釋我們要創建哪個數字 :) 只需傳遞數字 111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111 到構造函數將不起作用:Java 將嘗試將傳遞的數字填充到一種原始數據類型中,但它不適合其中任何一種。這就是為什麼使用字符串傳遞所需數字是一個不錯的選擇。這兩個類都可以自動從傳遞的字符串中提取數值。使用大數類時要記住的另一個要點是它們的對像是不可變的 ( Immutable )。由於您使用String類和基本類型(Integer、Long 等)的包裝類的經驗,您已經熟悉了不可變性。

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);

   }
}
控制台輸出:

11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 
如您所料,我們的號碼沒有改變。要執行加法運算,您必須創建一個新對象來接收運算結果。

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);

   }
}
控制台輸出:

11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111144444444
看,現在一切正常了 :) 順便說一下,您是否注意到加法運算看起來有多麼不尋常?

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 舍入行為

本主題有其單獨的部分,因為對大數字進行舍入和配置舍入行為並不是那麼簡單。您可以使用setScale()方法設置BigDecimal的小數位數。例如,假設我們希望數字111.5555555555 的小數點後保留三位數字。但是,我們不能通過將數字 3 作為參數傳遞給setScale()方法來實現我們想要的。如上所述,BigDecimal用於表示對計算精度有嚴格要求的數字。在當前形式中,我們的數字在小數點後有 10 位數字。我們要去掉其中的 7 個,只保留 3 個。因此,除了數字 3 之外,我們還必須通過舍入模式。 BigDecimal共有 8 種舍入模式。好多啊!但是,如果您真的需要微調計算的精度,您將擁有所需的一切。因此,這是BigDecimal提供的 8 種舍入模式:
  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 — 如果小數點後的數字 >= 0.5,則向上舍入

    
    0.55 -> setScale(1, ROUND_HALF_UP) -> 0.6
    0.54 -> setScale(1, ROUND_HALF_UP) -> 0.5
    
  5. ROUND_HALF_DOWN — 如果小數點後的數字 > 0.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()方法僅在 2 個數字具有相同的值和比例時才返回 true:讓我們比較DoubleBigDecimal類的equals()方法的行為:

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
如您所見,對於BigDecimal,數字 1.5 和 1.50 結果是不相等的!這正是因為BigDecimal類中equals()方法的實現細節。為了更準確地比較兩個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