La classe BigDecimal consente di utilizzare numeri a precisione decimale "arbitraria" (vedremo poi i limiti di questa definizione) e di eseguire calcoli aritmetici su di essi. Il suo utilizzo tuttavia non è immediato ed è facile commettere errori.

In questo articolo si cercherà di fare un po' di chiarezza sull'argomento, nella speranza di vedere diminuire la quantità di bug nei programmi che usano BigInteger e BigDecimal.

Cos'è

Il package java.math contiene due classi dedicate alla cosiddetta "aritmetica a precisione arbitraria": BigInteger e BigDecimal. Entrambe servono per gestire numeri la cui grandezza o precisione supera i limiti imposti dai tipi standard di Java: int, long, double, float, ecc.

java.math.BigInteger

Questa classe serve per rappresentare numeri interi immutabili di grandezza arbitraria; il massimo numero di cifre memorizzabili è Integer.MAX_VALUE.

java.math.BigDecimal

Questa classe serve per rappresentare numeri decimali immutabili di precisione arbitraria, ed è composta da due parti distinte:

  • valore unscaled (non scalato): è un valore di tipo BigInteger che rappresenta il numero privato della virgola;
  • scale (scala): è un numero di tipo int che indica quante posizioni decimali ci sono dopo la virgola.

Il valore di un numero BigDecimal è quindi: unscaled / 10^<sup>scale</sup>

A cosa serve

L'utilizzo di numeri in virgola mobile presenta dei brutti inconvenienti che li rendono non adatti per alcuni tipi di calcoli, in primis quelli finanziari (ma qualcuno ancora lo fa...capirete la mia rabbia allora...). Da sempre infatti i programmi gestionali di banche e assicurazioni utilizzano i cosiddetti numeri decimali a virgola fissa, i quali altro non sono che numeri interi sui quali si applica un numero fisso di posizioni decimali (anche molto elevato). Questo approccio consente di tenere sotto controllo alcune delle anomalie prodotte dai calcoli con numeri in virgola mobile (IEEE 754), ma a prezzo di una gestione più difficile.

java.math.BigDecimal serve proprio a gestire questo tipo di calcoli e si spinge oltre, fornendo molti metodi di arrotondamento e garantendo una precisione altissima.

Come si usa

Una caratteristica fondamentale di BigInteger e BigDecimal è che le loro istanze sono immutabili: tutte le operazioni restituiscono un nuovo oggetto contenente il risultato dell'operazione. Anche i metodi come setScale(), movePointLeft() / movePointRight() o negate(), che dal nome sembrerebbero in grado di modificare in qualche modo l'istanza, restituiscono un nuovo oggetto con il valore mutato.

Ecco un esempio:

package it.megadix.math;

import java.math.BigDecimal;

public class BigDecimalTest1 {
    public static void main(String[] args) {
        BigDecimal n1 = new BigDecimal("1000.123482248908085303458975309348");
        System.out.println("n1 = " + n1);
        BigDecimal n2 = new BigDecimal("2000.122837345398340801010291390210252");
        System.out.println("n2 = " + n2);

        BigDecimal resultAdd = n1.add(n2);
        System.out.println("n1 + n2 = " + resultAdd);

        BigDecimal resultMultiply = n1.multiply(n2);
        System.out.println("n1 * n2 = " + resultMultiply);

        BigDecimal resultDivide = n1.divide(n2, BigDecimal.ROUND_HALF_UP);
        System.out.println("n1 / n2 = " + resultDivide);
    }
}

Output del programma:

n1 = 1000.123482248908085303458975309348
n2 = 2000.122837345398340801010291390210252
n1 + n2 = 3000.246319594306426104469266699558252
n1 * n2 = 2000369.817011446171094293893027628836590866233492522879727390461035696
n1 / n2 = 0.500031029882290273171404981367

Alcune cose da notare:

  • utilizzo del costruttore BigDecimal(String). Questo costruttore accetta una notazione simile a quella inglese per i numeri decimali: segno, cifre prima della virgola, frazione ed esponente. Per i dettagli si rimanda alla documentazione ufficiale;
  • il risultato di ogni operazione è una nuova istanza di BigDecimal;
  • il metodo BigDecimal.toString() è compatibile con il costruttore BigDecimal(String).

Formattazione

La formattazione di numeri decimali è una faccenda molto complicata, entrano infatti in gioco fattori come:

  • lingua e località: ad esempio in inglese si usano le virgole per separare le miglialia e i punti per separare i decimali, in italiano e in francese invece è tutto il contrario;
  • alfabeto: i caratteri utilizzati per rappresentare i numeri possono variare (occidentale, ebraico, arabo, hindi...);
  • zeri iniziali e/o finali: alcuni esempi sono ".1", "0.1", ".10", "0.1000", ecc.;
  • segno "+" e "-";
  • esponenti e notazione scientifica;
  • ...non è abbastanza?

Di seguito un semplice esempio di formattazione che imposta la lingua e il numero di cifre decimali:

package it.megadix.math;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Locale;

public class BigDecimalTestFormat {
    public static void main(String[] args) {
        BigDecimal n = new BigDecimal("1000" +
                "." +
                "00000000000000000000000000000000000000000000000000" +
                "1" +
                "000");
        NumberFormat fmt_EN = NumberFormat.getNumberInstance(Locale.ENGLISH);
        fmt_EN.setMaximumFractionDigits(1000);

        NumberFormat fmt_IT = NumberFormat.getNumberInstance(Locale.ITALIAN);
        fmt_IT.setMaximumFractionDigits(1000);

        System.out.println(fmt_EN.format(n));
        System.out.println(fmt_IT.format(n));
    }
}

Output del programma:

1,000.000000000000000000000000000000000000000000000000001
1.000,000000000000000000000000000000000000000000000000001

Da notare:

  • la stringa utilizzata per creare il numero da formattare è stata spezzata su più righe per evidenzarne le varie parti;
  • la classe di formattazione è java.text.NumberFormat;
  • è stato utlilizzato il costruttore di NumberFormat che accetta come parametro un'istanza di java.util.Locale.

Sviluppi

Queste informazioni sono il minimo indispensabile per utilizzare la classe BigDecimal; per approfondimenti si consiglia di leggere attentamente la documentazione ufficiale della Sun e i molti tutorial disponibili su Internet.