CodeGym/Java Blog/Random-IT/BigDecimal in Java
John Squirrels
Livello 41
San Francisco

BigDecimal in Java

Pubblicato nel gruppo Random-IT
membri
CIAO! Nella lezione di oggi parleremo di grandi numeri. No, intendo DAVVERO GRANDE. In precedenza abbiamo ripetutamente incontrato la tabella degli intervalli di valori per i tipi di dati primitivi. Sembra così:
Tipo primitivo Dimensioni in memoria Intervallo di valori
byte 8 bit -128 a 127
corto 16 bit -32768 a 32767
char 16 bit da 0 a 65536
int 32 bit Da -2147483648 a 2147483647
lungo 64 bit -9223372036854775808 a 9223372036854775807
galleggiante 32 bit (2 elevato a -149) a ((2 elevato a -23) * 2 elevato a 127)
Doppio 64 bit (-2 elevato a 63) a ((2 elevato a 63) - 1)
booleano 8 (se utilizzato negli array), 32 (quando non utilizzato negli array) vero o falso
Il tipo di dati intero più spazioso è il long . Quando si tratta di numeri in virgola mobile, è il double . Ma cosa succede se il numero di cui abbiamo bisogno è così grande da non entrare nemmeno in un long ? Il tipo di dati Long ha una gamma piuttosto ampia di valori possibili, ma è ancora limitato a 64 bit. Cosa dobbiamo trovare se il nostro numero molto grande richiede 100 bit? Fortunatamente, non abbiamo bisogno di inventare nulla. Per casi come questo, Java ha due classi speciali: BigInteger (per i numeri interi) e BigDecimal(per numeri in virgola mobile). Cosa li rende speciali? Innanzitutto, in teoria, non hanno una dimensione massima. Diciamo "in teoria", perché non esistono computer con memoria infinita. E se il tuo programma crea un numero maggiore della quantità di memoria disponibile, ovviamente il programma non funzionerà. Ma tali casi sono improbabili. Di conseguenza, possiamo dire che BigInteger e BigDecimal possono rappresentare numeri di dimensione virtualmente illimitata. A cosa servono queste classi? Innanzitutto per calcoli con requisiti di accuratezza estremamente rigorosi. Ad esempio, la vita umana può dipendere dall'accuratezza dei calcoli in alcuni programmi (ad esempio software che controlla aeroplani, razzi o attrezzature mediche). Quindi, se la 150a cifra decimale è importante, allora BigDecimalè la scelta migliore. Inoltre, gli oggetti di questa classe sono spesso utilizzati nel mondo della finanza, dove è estremamente importante anche il calcolo accurato anche dei valori più piccoli. Come lavori con gli oggetti BigInteger e BigDecimal e hai bisogno di conoscerli? Gli oggetti di queste classi vengono creati in questo modo:
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);
   }
}
Passare una stringa al costruttore è solo una delle possibili opzioni. Qui usiamo le stringhe, perché i nostri numeri superano i valori massimi per long e double , e abbiamo bisogno di un modo per spiegare al compilatore quale numero vogliamo creare :) Semplicemente passando il numero 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 111111111111111111111111111111111111111111111111111111111111111111 al costruttore non funzionerà: Java proverà a stipare il numero passato in uno dei tipi di dati primitivi, ma non si adatterà a nessuno di essi. Ecco perché usare una stringa per passare il numero desiderato è una buona opzione. Entrambe le classi possono estrarre automaticamente valori numerici dalle stringhe passate. Un altro punto importante da ricordare quando si lavora con classi con numeri grandi è che i loro oggetti sono immutabili ( Immutable ). Hai già familiarità con l'immutabilità grazie alla tua esperienza con la classe String e le classi wrapper per i tipi primitivi (Integer, Long, ecc.).
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);

   }
}
Uscita console:
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
Come ci si aspetterebbe, il nostro numero non è cambiato. Per eseguire l'operazione di addizione, è necessario creare un nuovo oggetto per ricevere il risultato dell'operazione.
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);

   }
}
Uscita console:
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111144444444
Vedi, ora tutto funziona come dovrebbe :) A proposito, hai notato quanto sia insolita l'operazione di addizione?
BigInteger result = integer.add(BigInteger.valueOf(33333333));
Questo è un altro punto importante. Le classi con numeri grandi non usano gli operatori + - * /. Invece, forniscono una serie di metodi. Facciamo conoscenza con i principali (come sempre, puoi trovare un elenco completo dei metodi nella documentazione di Oracle: qui e qui ).
  1. metodi per operazioni aritmetiche: add() , subtract() , multiply() , divide() . Questi metodi vengono utilizzati per eseguire rispettivamente addizioni, sottrazioni, moltiplicazioni e divisioni.

  2. doubleValue() , intValue() , floatValue() , longValue() , ecc. sono usati per convertire un numero grande in uno dei tipi primitivi di Java. Fai attenzione quando usi questi metodi. Non dimenticare le differenze nella dimensione dei bit!

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

    Uscita console:

    8198552921648689607
  3. min() e max() consentono di trovare il valore minimo e massimo di due grandi numeri.
    Si noti che questi metodi non sono statici!

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

    Uscita console:

    222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222

Comportamento di arrotondamento BigDecimal

Questo argomento ha una sua sezione separata, poiché l'arrotondamento di numeri grandi e la configurazione del comportamento di arrotondamento non sono così semplici. È possibile utilizzare il metodo setScale() per impostare il numero di posizioni decimali per un BigDecimal . Ad esempio, supponiamo di volere che il numero 111.5555555555 abbia tre cifre dopo la virgola. Tuttavia, non possiamo ottenere ciò che vogliamo passando il numero 3 come argomento al metodo setScale() . Come accennato in precedenza, BigDecimalserve per rappresentare numeri con severi requisiti di precisione computazionale. Nella sua forma attuale, il nostro numero ha 10 cifre dopo la virgola. Vogliamo eliminarne 7 e mantenerne solo 3. Di conseguenza, oltre al numero 3, dobbiamo passare la modalità di arrotondamento. BigDecimal ha un totale di 8 modalità di arrotondamento. Questo è molto! Ma se hai davvero bisogno di mettere a punto la precisione dei tuoi calcoli, avrai tutto ciò di cui hai bisogno. Quindi, ecco le 8 modalità di arrotondamento offerte da BigDecimal :
  1. ROUND_CEILING — arrotonda per eccesso

    111.5555555555 -> setScale(3, ROUND_CEILING) -> 111.556
  2. ROUND_DOWN — arrotonda verso lo zero

    111.5555555555 -> setScale(3, ROUND_DOWN) -> 111.555
  3. ROUND_FLOOR — arrotonda per difetto

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

  4. ROUND_HALF_UP — arrotonda per eccesso se il numero dopo la virgola >= 0,5

    0.55 -> setScale(1, ROUND_HALF_UP) -> 0.6
    0.54 -> setScale(1, ROUND_HALF_UP) -> 0.5
  5. ROUND_HALF_DOWN — arrotonda per eccesso se il numero dopo la virgola > 0,5

    0.55 -> setScale(1, ROUND_HALF_DOWN) -> 0.5
    0.56 -> setScale(1, ROUND_HALF_DOWN) -> 0.6
  6. ROUND_HALF_EVEN — l'arrotondamento dipende dal numero a sinistra della virgola decimale. Se il numero a sinistra è pari, l'arrotondamento sarà per difetto. Se il numero a sinistra della virgola è dispari, l'arrotondamento sarà per eccesso.

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

    Il numero a sinistra della cifra decimale è 2 (pari). Il numero è arrotondato per difetto. Vogliamo 0 cifre decimali, quindi il risultato è 2.

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

    Il numero a sinistra della virgola è 3 (dispari). Il numero è arrotondato. Vogliamo 0 cifre decimali, quindi il risultato è 4.

  7. ROUND_UNNECCESSARY — Questa modalità viene utilizzata quando è necessario passare una modalità di arrotondamento a un metodo, ma non è necessario arrotondare il numero. Se si tenta di arrotondare un numero con la modalità ROUND_UNNECCESSARY impostata, viene generata un'eccezione ArithmeticException.

    3.51 -> setScale(1, ROUND_UNNECCESSARY) -> ArithmeticException
  8. ROUND_UP — arrotonda partendo da zero.

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

Confronto di grandi numeri

Anche questo è importante. Ricorderai che usiamo il metodo equals() per confrontare gli oggetti in Java. L'implementazione è fornita dal linguaggio stesso (per le classi Java standard) o sovrascritta dal programmatore. Ma nel caso di oggetti BigDecimal , non è consigliabile utilizzare il metodo equals() per i confronti. Questo perché il metodo BigDecimal.equals() restituisce true solo se i 2 numeri hanno lo stesso valore e scala: Confrontiamo il comportamento del metodo equals() per le classi Double e 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));

   }
}
Uscita console:
true
false
Come puoi vedere, per BigDecimal , i numeri 1.5 e 1.50 si sono rivelati disuguali! Questo era proprio a causa delle specifiche dell'implementazione del metodo equals() nella classe BigDecimal . Per un confronto più accurato di due oggetti BigDecimal , è meglio utilizzare il metodo 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));

   }
}
Uscita console:
0
Il metodo compareTo() ha restituito 0, il che significa che 1,5 e 1,50 sono uguali. E questo è il risultato che ci aspettavamo! :) Questo conclude la nostra lezione di oggi. Ora è il momento di tornare ai compiti! :)
Commenti
  • Popolari
  • Nuovi
  • Vecchi
Devi avere effettuato l'accesso per lasciare un commento
Questa pagina non ha ancora commenti