CodeGym/Blog Java/Random-PL/BigDecimal w Javie
Autor
Oleksandr Miadelets
Head of Developers Team at CodeGym

BigDecimal w Javie

Opublikowano w grupie Random-PL
Cześć! Na dzisiejszej lekcji porozmawiamy o dużych liczbach. Nie, mam na myśli NAPRAWDĘ DUŻE. Wcześniej wielokrotnie spotykaliśmy się z tabelą zakresów wartości dla prymitywnych typów danych. To wygląda tak:
Typ pierwotny Rozmiar w pamięci Zakres wartości
bajt 8 bitowy -128 do 127
krótki 16 bitów -32768 do 32767
zwęglać 16 bitów od 0 do 65536
int 32 bity -2147483648 do 2147483647
długi 64-bitowy -9223372036854775808 do 9223372036854775807
platforma 32 bity (2 do potęgi -149) do ((2 do potęgi -23) * 2 do potęgi 127)
podwójnie 64-bitowy (-2 do potęgi 63) do ((2 do potęgi 63) - 1)
logiczna 8 (gdy jest używany w tablicach), 32 (gdy nie jest używany w tablicach) prawda czy fałsz
Najbardziej pojemny typ danych całkowitych to long . Jeśli chodzi o liczby zmiennoprzecinkowe, jest to double . Ale co, jeśli potrzebna nam liczba jest tak duża, że ​​nie mieści się nawet w długim ? Typ danych Long ma dość duży zakres możliwych wartości, ale wciąż jest ograniczony do 64 bitów. Co musimy wymyślić, jeśli nasza bardzo duża liczba wymaga 100 bitów? Na szczęście nie musimy niczego wymyślać. W takich przypadkach Java ma dwie specjalne klasy: BigInteger (dla liczb całkowitych) i BigDecimal(dla liczb zmiennoprzecinkowych). Co czyni je wyjątkowymi? Przede wszystkim teoretycznie nie mają maksymalnego rozmiaru. Mówimy „w teorii”, ponieważ nie ma komputerów z nieskończoną pamięcią. A jeśli twój program utworzy liczbę większą niż ilość dostępnej pamięci, to oczywiście nie będzie działał. Ale takie przypadki są mało prawdopodobne. W rezultacie możemy powiedzieć, że BigInteger i BigDecimal mogą reprezentować liczby o praktycznie nieograniczonej wielkości. Do czego służą te klasy? Przede wszystkim do obliczeń o wyjątkowo rygorystycznych wymaganiach co do dokładności. Na przykład ludzkie życie może zależeć od dokładności obliczeń niektórych programów (np. oprogramowania sterującego samolotami, rakietami, czy sprzętem medycznym). Więc jeśli 150. miejsce po przecinku jest ważne, to BigDecimaljest najlepszym wyborem. Ponadto obiekty tej klasy są często wykorzystywane w świecie finansów, gdzie niezwykle ważne jest również dokładne wyliczenie nawet najmniejszych wartości. Jak pracujesz z obiektami BigInteger i BigDecimal i czy musisz o nich wiedzieć? Obiekty tych klas są tworzone 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);
   }
}
Przekazanie ciągu do konstruktora to tylko jedna z możliwych opcji. Tutaj używamy ciągów znaków, ponieważ nasze liczby przekraczają maksymalne wartości dla long i double , i potrzebujemy jakiegoś sposobu, aby wyjaśnić kompilatorowi, którą liczbę chcemy utworzyć :) 11111111111111111111111111111111111111111111111111111111111111111111111111 do konstruktora nie zadziała: Java spróbuje wcisnąć przekazaną liczbę do jednego z prymitywnych typów danych, ale nie będzie pasować do żadnego z nich. Dlatego dobrym rozwiązaniem jest użycie ciągu znaków do przekazania żądanej liczby. Obie klasy mogą 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 ). Niezmienność jest Ci już znana dzięki doświadczeniu z klasą String i klasami opakowującymi dla typów pierwotnych (Integer, Long itp.).
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:
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
Jak można się spodziewać, nasz numer się nie zmienił. Aby wykonać operację dodawania, musisz utworzyć nowy obiekt, aby otrzymać wynik operacji.
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:
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111144444444
Widzisz, teraz wszystko działa tak, jak powinno :) 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 z dużymi liczbami nie używają operatorów + - * /. Zamiast tego udostępniają zestaw metod. Zapoznajmy się z głównymi (jak zwykle pełną listę metod znajdziecie w dokumentacji Oracle: tutaj i tutaj ).
  1. metody operacji arytmetycznych: add() , subtract() , mnożenie() , dzielenie() . Metody te służą odpowiednio do dodawania, odejmowania, mnożenia i dzielenia.

  2. doubleValue() , intValue() , floatValue() , longValue() itd. służą do konwersji dużej liczby na jeden z prymitywnych typów Javy. Zachowaj ostrożność podczas korzystania z tych metod. Nie zapomnij o różnicach w wielkości bitów!

    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 dużych liczb.
    Pamiętaj, że te 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

Zachowanie zaokrąglania BigDecimal

Ten temat ma oddzielną sekcję, ponieważ zaokrąglanie dużych liczb i konfigurowanie zachowania zaokrąglania nie jest takie proste. Możesz użyć metody setScale() , aby ustawić liczbę miejsc dziesiętnych dla BigDecimal . Załóżmy na przykład, że chcemy, aby liczba 111,5555555555 miała trzy cyfry po przecinku. Nie możemy jednak osiągnąć tego, czego chcemy, przekazując liczbę 3 jako argument do metody setScale() . Jak wspomniano powyżej, BigDecimalsłuży do reprezentowania liczb z surowymi wymaganiami dotyczącymi precyzji obliczeniowej. W obecnej formie nasz numer ma 10 cyfr po przecinku. Chcemy zrzucić 7 z nich i zachować tylko 3. W związku z tym oprócz liczby 3 musimy przejść tryb zaokrąglania. BigDecimal ma łącznie 8 trybów zaokrąglania. To dużo! Ale jeśli naprawdę potrzebujesz dostroić precyzję swoich obliczeń, będziesz miał wszystko, czego potrzebujesz. Oto 8 trybów zaokrąglania oferowanych przez BigDecimal :
  1. ROUND_CEILING — zaokrągla w górę

    111.5555555555 -> setScale(3, ROUND_CEILING) -> 111.556
  2. ROUND_DOWN — zaokrągla do zera

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

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

  4. ROUND_HALF_UP — zaokrągla w górę, jeśli 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ągla w górę, jeśli 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 zależy od liczby po lewej stronie przecinka dziesiętnego. Jeśli liczba po lewej stronie jest parzysta, zaokrąglenie będzie mniejsze. Jeśli liczba po lewej stronie przecinka dziesiętnego jest nieparzysta, zaokrąglenie zostanie wykonane w górę.

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

    Liczba po lewej stronie przecinka to 2 (parzyste). Liczba jest zaokrąglana w dół. Chcemy 0 miejsc po przecinku, więc wynikiem jest 2.

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

    Liczba po lewej stronie przecinka dziesiętnego to 3 (nieparzysta). Liczba jest zaokrąglana w górę. Chcemy 0 miejsc po przecinku, więc wynikiem jest 4.

  7. ROUND_UNNECCESSARY — Ten tryb jest używany, gdy musisz przekazać metodzie tryb zaokrąglania, ale liczba nie musi być zaokrąglana. Jeśli spróbujesz zaokrąglić liczbę z ustawionym trybem ROUND_UNNECCESSARY, zostanie zgłoszony wyjątek ArithmeticException.

    3.51 -> setScale(1, ROUND_UNNECCESSARY) -> ArithmeticException
  8. ROUND_UP — zaokrągla od zera.

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

Porównywanie dużych liczb

To też jest ważne. Pamiętasz, że używamy metody equals() do porównywania obiektów w Javie. Implementacja jest dostarczana przez sam język (dla standardowych klas Java) lub nadpisywana przez programistę. Ale w przypadku obiektów BigDecimal nie zaleca się używania metody equals() do porównań. Dzieje się tak, ponieważ metoda BigDecimal.equals() zwraca wartość true tylko wtedy, gdy dwie liczby mają tę samą wartość i skalę: Porównajmy zachowanie metody equals() dla klas Double i 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ć, dla BigDecimal liczby 1.5 i 1.50 okazały się nierówne! Wynikało to właśnie ze specyfiki implementacji metody equals() w klasie BigDecimal . Aby uzyskać dokładniejsze porównanie dwóch obiektów BigDecimal , lepiej jest użyć metody 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, że ​​1,5 i 1,50 są równe. I takiego wyniku oczekiwaliśmy! :) To kończy naszą dzisiejszą lekcję. Teraz czas wrócić do zadań! :)
Komentarze
  • Popularne
  • Najnowsze
  • Najstarsze
Musisz się zalogować, aby dodać komentarz
Ta strona nie ma jeszcze żadnych komentarzy