Numeri in Java
Lavora con i tipi numerici in Java — int, long, float, double — e comprendi precisione, overflow e la classe Number.
Java distingue i numeri interi da quelli in virgola mobile e, all'interno di ciascuna famiglia, offre diverse dimensioni. Scegliere il tipo corretto è importante sia per la correttezza (precisione, intervallo) sia per le prestazioni. Questo capitolo è una guida focalizzata sui tipi numerici, i loro letterali, le conversioni e le insidie che sorprendono i principianti. Si presuppone che tu sappia già come dichiarare una variabile e abbia già visto l'insieme completo dei tipi di dati primitivi.
I tipi primitivi numerici
| Tipo | Bit | Intervallo | Stile letterale predefinito |
|---|---|---|---|
byte | 8 | da -128 a 127 | byte b = 10; |
short | 16 | da -32.768 a 32.767 | short s = 30_000; |
int | 32 | da -2³¹ a 2³¹-1 (≈ ±2,1 miliardi) | int i = 1_000_000; |
long | 64 | da -2⁶³ a 2⁶³-1 | long l = 9_000L; |
float | 32 | IEEE 754 singola precisione (~7 cifre) | float f = 3.14f; |
double | 64 | IEEE 754 doppia precisione (~16 cifre) | double d = 3.14; |
Nel Java di tutti i giorni:
- Usa
intper contatori, indici e la maggior parte dei valori interi. - Usa
longquando potresti superare ~2 miliardi (dimensioni di file, millisecondi dall'epoch, generatori di ID). - Usa
doubleper i calcoli in virgola mobile. - Usa
floatsolo quando la memoria è scarsa o un'API lo richiede.
Letterali interi
Le cifre semplici sono int:
int answer = 42;
int million = 1_000_000; // underscores are ignoredSuffisso L (maiuscolo preferito) per long:
long big = 9_000_000_000L;Prefisso 0x per esadecimale, 0b per binario, 0 per ottale:
int hex = 0xFF; // 255
int bin = 0b1010; // 10
int oct = 0755; // 493 — be careful, easy to write by accident(I letterali ottali sono una fonte frequente di bug — 0123 non è 123, è 83. Evita gli zeri iniziali a meno che tu non voglia davvero indicare un valore ottale.)
Letterali in virgola mobile
I decimali semplici sono double:
double pi = 3.14159;
double e = 2.71828;Suffisso f per float, d per double esplicito (facoltativo):
float pif = 3.14f;
double pid = 3.14d;La notazione scientifica funziona:
double avogadro = 6.022e23;
double tiny = 1.0e-9;Conversione tra tipi numerici
Java esegue automaticamente una conversione widening quando un tipo più piccolo confluisce in uno più grande (non si può perdere nessun dato):
int x = 7;
double promoted = x; // 7.0 — int widens to double automaticallyIl percorso inverso è il narrowing: il valore potrebbe non entrare, quindi Java ti obbliga a scrivere un cast esplicito e scarta silenziosamente ciò che non ci sta. Si tratta di troncamento, non di arrotondamento:
double d = 9.99;
int i = (int) d; // 9 — the fraction is dropped, never rounded
long big = 9_000_000_000L;
int truncated = (int) big; // 410065408 — high bits lost, value is garbagePer questo motivo, restringi il tipo solo quando sei certo che il valore ci stia. Per arrotondare un double all'intero più vicino invece di troncare, usa Math.round (vedi Java Math).
Le conversioni avvengono anche all'interno delle espressioni. La divisione intera scarta il resto, il che sorprende i principianti:
System.out.println(7 / 2); // 3 — both operands are int, so int division
System.out.println(7 / 2.0); // 3.5 — one double operand promotes the whole expressionPer le regole complete sui cast espliciti, consulta Java Type Casting.
Overflow
L'aritmetica intera va in wrap silenziosamente oltre i limiti del tipo. Non viene lanciata alcuna eccezione:
int max = Integer.MAX_VALUE;
System.out.println(max + 1); // -2147483648 (Integer.MIN_VALUE)Per l'aritmetica con controllo dell'overflow, usa la famiglia Math.exact:
Math.addExact(Integer.MAX_VALUE, 1); // throws ArithmeticException
Math.multiplyExact(100_000, 100_000); // throws tooUtile quando la correttezza è più importante delle prestazioni — calcoli finanziari, dimensioni di allocazione, ecc.
Precisione in virgola mobile
double e float seguono lo standard IEEE 754 binario — non possono rappresentare la maggior parte delle frazioni decimali in modo esatto:
System.out.println(0.1 + 0.2); // 0.30000000000000004Non confrontare mai i valori in virgola mobile con ==. Confronta entro una tolleranza:
double diff = Math.abs(a - b);
if (diff < 1e-9) { /* close enough */ }Per importi di denaro o in qualsiasi caso tu abbia bisogno di aritmetica decimale esatta, usa BigDecimal:
import java.math.BigDecimal;
BigDecimal sum = new BigDecimal("0.1").add(new BigDecimal("0.2"));
System.out.println(sum); // 0.3Passa sempre stringhe a new BigDecimal(...). Il costruttore con double reintrodurrebbe l'imprecisione binaria.
Valori speciali in virgola mobile
double ha tre valori che non sono numeri ordinari:
double posInf = Double.POSITIVE_INFINITY;
double negInf = Double.NEGATIVE_INFINITY;
double nan = Double.NaN;
System.out.println(1.0 / 0); // Infinity
System.out.println(-1.0 / 0); // -Infinity
System.out.println(0.0 / 0); // NaN
System.out.println(nan == nan); // false — NaN compares unequal to everything
System.out.println(Double.isNaN(nan)); // trueUsa Double.isNaN(x) per verificare il NaN; == non funzionerà.
Le classi wrapper
Ogni tipo primitivo ha una classe wrapper corrispondente: Byte, Short, Integer, Long, Float, Double. Le wrapper estendono la classe astratta Number. Sono necessarie quando:
- Si usano collezioni che contengono oggetti:
List<Integer>,Map<String, Double>. - Si effettua il parsing da stringhe:
Integer.parseInt("42"),Double.parseDouble("3.14"). - Si converte in un tipo numerico diverso tramite
.intValue(),.doubleValue(), ecc.
Java esegue l'autoboxing tra primitivo e wrapper automaticamente:
List<Integer> ids = new ArrayList<>();
ids.add(42); // autobox: int → Integer
int first = ids.get(0); // unbox: Integer → intBigInteger e BigDecimal
Quando i numeri possono essere più grandi di long o hai bisogno di precisione decimale:
import java.math.BigInteger;
BigInteger huge = new BigInteger("123456789012345678901234567890");
huge.multiply(huge);
BigDecimal price = new BigDecimal("19.95");
BigDecimal tax = price.multiply(new BigDecimal("0.07"));Sono più lenti dei tipi primitivi, ma per i casi che li richiedono è il compromesso giusto.
Una dimostrazione
Cosa c'è dopo
- Java Math —
Math.abs,Math.round,Math.powe il resto del toolkit numerico. - Java Operators — come si comportano gli operatori aritmetici, di confronto e bit a bit su questi tipi.
- Java Booleans — il tipo a due valori di Java, con molti meno dettagli.