JavaRush /Blog Java /Random-PL /BigDecimal w Javie

BigDecimal w Javie

Opublikowano w grupie Random-PL
Cześć! Na dzisiejszym wykładzie będziemy mówić o dużych liczbach. Nie, o NAPRAWDĘ DUŻYCH. Wcześniej tabelę zakresów wartości dla prymitywnych typów danych widzieliśmy więcej niż raz. To wygląda tak:
Typ prymitywny Rozmiar w pamięci Zakres wartości
bajt 8 bitowy -128 do 127
krótki 16-bitowy do -32768 do 32767
zwęglać 16-bitowy od 0 do 65536
wew 32 bity od -2147483648 do 2147483647
długi 64 bity od -9223372036854775808 do 9223372036854775807
platforma 32 bity od (2 do potęgi -149) do ((2-2 do potęgi -23)*2 do potęgi 127)
podwójnie 64 bity od (-2 do potęgi 63) do ((2 do potęgi 63) - 1)
wartość logiczna 8 (w przypadku użycia w tablicach), 32 (w przypadku użycia w przypadku innych niż tablice) prawda czy fałsz
Jeśli mówimy o liczbach całkowitych, najbardziej pojemnym typem danych jest long , a jeśli mówimy o liczbach zmiennoprzecinkowych, double . Co jednak, jeśli liczba, której potrzebujemy, jest tak duża, że ​​nawet nie mieści się w niej długo ? Zakres możliwych wartości Long jest dość duży, ale nadal ograniczony do pewnego rozmiaru - 64 bity. Co możemy wymyślić, jeśli nasza Bardzo duża liczba waży 100 bitów? Na szczęście nie trzeba niczego wymyślać. W Javie stworzono dla takich przypadków dwie specjalne klasy – BigInteger (dla liczb całkowitych) i BigDecimal (dla liczb zmiennoprzecinkowych). Jaka jest ich cecha? Przede wszystkim teoretycznie nie mają one maksymalnego rozmiaru. Teoretycznie, bo nie ma komputerów z nieskończoną pamięcią. A jeśli utworzysz w programie liczbę większą niż rozmiar pamięci komputera, to oczywiście program nie będzie działać. Ale takie przypadki są mało prawdopodobne. Można zatem powiedzieć, że wielkość liczb BigIntegerjest BigDecimalpraktycznie nieograniczona. Do czego służą te zajęcia? Przede wszystkim do obliczeń o niezwykle wysokich wymaganiach dotyczących dokładności. Istnieją np. programy, w których od dokładności obliczeń może zależeć ludzkie życie (oprogramowanie dla samolotów i rakiet czy dla sprzętu medycznego). Dlatego jeśli nawet 150. miejsce po przecinku odgrywa ważną rolę, BigDecimaljest to najlepszy wybór. Poza tym dość często obiekty te wykorzystywane są w świecie finansów, gdzie niezwykle istotna jest także dokładność obliczeń co do najmniejszych wartości. Jak pracować z przedmiotami BigIntegeri BigDecimalo czym warto o nich pamiętać? Obiekty tych klas tworzone są w następujący sposób:
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);
   }
}
Przekazywanie ciągu jako parametru jest tylko jednym z możliwych konstruktorów. Here we use strings because our numbers exceed the maximum values long​​and double, and somehow we need to explain to the compiler exactly what number we want to get :) Just pass the number 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 to the constructor 1111111111111111111111111111111111111111111111111111111111111111111 will not work: Java will try to „dopasuj” przekazaną liczbę do jednego z prymitywnych typów danych, ale nie będzie ona pasować do żadnego z nich. Dlatego dobrym rozwiązaniem jest użycie ciągu znaków do przekazania żądanej liczby. Obie klasy potrafią automatycznie wyodrębniać wartości liczbowe z przekazanych ciągów znaków. Inną ważną kwestią, o której należy pamiętać podczas pracy z klasami o dużej liczbie, jest to, że ich obiekty są niezmienne ( Immutable) . Zasadę niezmienności znasz już dobrze na przykładzie klasy Stringi klas opakowujących dla prymitywów (Integer, Long i inne).
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);

   }
}
Wyjście konsoli:

1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
Nasze liczby nie zmieniły się, jak można się spodziewać. Aby operacja dodawania zakończyła się sukcesem należy utworzyć nowy obiekt i przypisać do niego wynik dodawania.
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);

   }
}
Wyjście konsoli:

1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111144444444
Teraz wszystko działa jak należy :) Swoją drogą, czy zauważyłeś jak nietypowo wygląda operacja dodawania?
BigInteger result = integer.add(BigInteger.valueOf(33333333));
To kolejny ważny punkt. Klasy o dużych liczbach nie używają operatorów +-*/ w swoim działaniu, lecz zamiast tego udostępniają zestaw metod. Przyjrzyjmy się głównym z nich (pełną listę metod jak zawsze znajdziesz w dokumentacji Oracle: tutaj i tutaj ).
  1. metody wykonywania operacji arytmetycznych: add() , subtract(), multiply(), divide(). Używane odpowiednio do operacji dodawania, odejmowania, mnożenia i dzielenia.

  2. doubleValue(), intValue(), floatValue()itp longValue(). - Służy do konwersji dużej liczby na pierwotny typ Java. Zachowaj ostrożność podczas ich używania i pamiętaj o różnicy w pojemności!

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

    Wyjście konsoli:

    
    8198552921648689607
  3. min()i max()- pozwalają znaleźć minimalną i maksymalną wartość dwóch przekazanych dużych liczb.
    Uwaga: metody nie są statyczne!

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

    Wyjście konsoli:

    
    222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222

Kontrola zaokrąglania BigDecimal

Temat ten poświęcono osobnemu rozdziałowi, ponieważ zaokrąglanie dużych liczb i ich korygowanie nie jest taką prostą rzeczą. Możesz ustawić liczbę miejsc dziesiętnych dla liczby BigDecimalza pomocą setScale(). Przykładowo chcemy ustawić dokładność liczby 111,5555555555 na trzy miejsca po przecinku. Nie uda nam się jednak przekazać liczby 3 jako argumentu metody setScale()i tym samym rozwiązać naszego problemu. Jak wspomniano powyżej, BigDecimalsą to liczby do obliczeń o zwiększonej dokładności. W obecnej formie nasza liczba ma 10 miejsc po przecinku. Chcemy odrzucić ich 7 i zostawić tylko 3. Dlatego oprócz liczby 3 musimy przekazać jako parametr tryb zaokrąglania . Łącznie dostępnych jest 8 trybów zaokrąglania BigDecimal. Sporo! Ale jeśli chcesz naprawdę dostroić dokładność obliczeń w programie, będziesz mieć wszystko, czego potrzebujesz. Oto 8 trybów zaokrąglania dostępnych w BigDecimal:
  1. ROUND_CEILING- zaokrąglać w górę

    111.5555555555 -> setScale(3, ROUND_CEILING) -> 111.556
  2. ROUND_DOWN- usuwanie wydzieliny

    111.5555555555 -> setScale(3, ROUND_DOWN) -> 111.555
  3. ROUND_FLOOR- zaokrąglenie w dół

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

  4. ROUND_HALF_UP— zaokrąglenie w górę, jeżeli liczba po przecinku >= 0,5

    0.55 -> setScale(1, ROUND_HALF_UP) -> 0.6
    0.54 -> setScale(1, ROUND_HALF_UP) -> 0.5
  5. ROUND_HALF_DOWN— zaokrąglenie w górę, jeżeli liczba po przecinku > 0,5

    0.55 -> setScale(1, ROUND_HALF_DOWN) -> 0.5
    0.56 -> setScale(1, ROUND_HALF_DOWN) -> 0.6
  6. ROUND_HALF_EVEN— zaokrąglenie będzie zależne od liczby znajdującej się po lewej stronie przecinka dziesiętnego. Jeżeli liczba po lewej stronie jest parzysta, wówczas zaokrąglenie zostanie wykonane w dół. Jeśli liczba po lewej stronie przecinka dziesiętnego jest nieparzysta, zostanie zaokrąglona w górę.

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

    Liczba po lewej stronie przecinka dziesiętnego – 2 – jest parzysta. Zaokrąglanie następuje w dół. Ponieważ wymagamy 0 miejsc po przecinku, wynikiem będzie 2.

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

    Liczba po lewej stronie przecinka dziesiętnego – 3 – jest nieparzysta. Zaokrąglenie następuje w górę. Ponieważ wymagamy 0 miejsc po przecinku, wynikiem będzie 4.

  7. ROUND_UNNECCESSARY— stosowane w przypadkach, gdy trzeba przekazać tryb zaokrąglania do jakiejś metody, ale liczba nie musi być zaokrąglana. Jeśli spróbujesz zaokrąglić liczbę, gdy ustawiony jest tryb ROUND_UNNECCESSARY, zostanie zgłoszony wyjątek ArithmeticException.

    3.51 -> setScale(1, ROUND_UNNECCESSARY) -> ArithmeticException
  8. ROUND_UP- zaokrąglać w górę.

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

Porównanie dużych liczb

To pytanie jest również ważne. Pamiętasz już, że metoda ta służy do porównywania obiektów w Javie equals(). Jest albo dostarczany przez sam język (w przypadku klas wbudowanych w Javie), albo jest zastępowany przez programistę. Jednak w przypadku obiektów klas nie zaleca się BigDecimalstosowania tej metody equals()do porównania. Dzieje się tak dlatego, że BigDecimal.equals()metoda dwuliczbowa zwraca wartość true tylko wtedy, gdy obie liczby mają tę samą wartość i skalę : Porównajmy zachowanie metod equals()y Doublei 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));

   }
}
Wyjście konsoli:

true
false
Jak widać liczby 1,5 i 1,50 w przypadku c BigDecimalokazały się nierówne! Stało się tak właśnie ze względu na specyfikę metody equals()w klasie BigDecimal. Aby uzyskać dokładniejsze porównanie obu, BigDecimallepiej zastosować metodę 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));

   }
}
Wyjście konsoli:

0
Metoda compareTo()zwróciła 0, co oznacza równe 1,5 i 1,50. Na taki wynik liczyliśmy! :) Na tym kończymy naszą dzisiejszą lekcję. Czas wrócić do zadań! :)
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION