View Full Version : java come non troncare numeri
leggendo dei numeri da un file e convertendoli in double usando num = Double.parseDouble(linea); ho notato che quando li stampo questi non sono uguali ad esempio
-0.01976446502439700405
diventa
-0.0197644650243970040
oppure
4512014.1482265731234567890
diventa
4512014.148226573
come posso risolvere, mi servono come numeri senza essere troncati perchè poi devo sommarli/sottrarli???
:help: :help: :help:
Usa BigDecimal
Per le operazioni ha dei metodi al posto degli operatori. Sono immutable quindi i metodi-operatori non alterano il valore dell'istanza ma restituiscono nuovi BigDecimal che contengono il risultato. A parte questo sono una gioia :D.
grazie!!! ho provato ma ho alcuni problemi, ecco il codice:
import java.math.BigDecimal;
public class Grandi {
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal("6666665.2222222224");
BigDecimal num2 = new BigDecimal("2222223.2222222224");
BigDecimal num3 = new BigDecimal("1234567.0987654321");
BigDecimal num4 = new BigDecimal("2.0");
System.out.println("num1 = " +num1);
System.out.println("num2 = " +num2);
BigDecimal somma = num1.add(num2);
System.out.println("somma = " +somma);
BigDecimal moltiplica = num1.multiply(num2);
System.out.println("moltiplicazione = " +moltiplica);
BigDecimal divisione_1 = somma.divide(num4);
System.out.println("divisione_1 = " +divisione_1);
BigDecimal test = moltiplica;
System.out.println("test deve essere uguale a moltiplica = " +test);
BigDecimal divisione_2 = num1.divide(num2);
System.out.println("divisione_2 = " +divisione_2);
}
}
ho questo errore:
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1513)
at Grandi.main(Grandi.java:24)
inoltre se volessi fare (num1+num2+test )/3 come dovrei fare ??? :help:
grazie ;)
Se dividi allora devi specificare un tipo di arrotondamento perchè il meccanismo predefinito è ultra-conservativo: se non può calcolare un risultato esatto allora spara un'eccezione.
C'è un metodo ad hoc in BigDecimal, il divide che accetta un RoundingMode. In pratica anzichè:
BigDecimal r = a.divide(b);
fai
BigDecimal r = a.divide(b, RoundingMode.HALF_EVEN);
O un altro RoundingMode, devi vedere tu qual'è il più adatto.
ho provato con
BigDecimal divisione_2 = num1.divide(num2, BigDecimal.ROUND_HALF_UP);
che ha arrotondato il risultato a 3 senza avere nessuna eccezione :D
ma per fare (num1+num2+test+ ...)/3 come dovrei fare perchè tutti gli esempi che ho trovato fanno sempre operazioni soltanto tra due valori, inoltre posso dividere direttamente per 3 oppure questo dovrà essere inizializzato in qualche altra variabile. grazie mille!!! :)
Supponendo che a, b, c e d siano BigDecimal dirai:
BigDecimal r = a.add(b).add(c).divide(d, RoundingMode.HALF_EVEN);
che tradotto in simboli sarebbe:
(a + b + c) / d
se a, b, c sono BigDecimal e vorrei dividerli per 3
posso fare soltanto così:
BigDecimal tre_ = new BigDecimal("3");
BigDecimal risultato = a.add(b).add(c).divide(tre_, RoundingMode.HALF_EVEN);
cioè non c'è un modo più veloce, tipo come per i float
float a=1;
float b=2;
float tot=(a+b)/2;
System.out.println(tot);
:help:
cioè non c'è un modo più veloce, tipo come per i floatNo ... i metodi sono quelli!
Per costruire BigDecimal con valori predefiniti (costanti), parti da un int/long/double, non da una stringa (se vai a vedere BigDecimal ha una caterva di costruttori!)
se faccio così e corretto
import java.math.BigDecimal;
public class Prova {
public static void main(String[] args) {
int i = 8;
BigDecimal bd = new BigDecimal(String.valueOf(i));
System.out.println(bd);
}
}
int i = 8;
BigDecimal bd = new BigDecimal(String.valueOf(i));Non fai prima con new BigDecimal(8) (con Java 5), altrimenti new BigDecimal(8.0)???
Ma sei andato a vedere i costruttori di BigDecimal???
Usa sempre il costruttore che richiede una stringa e sei a posto.
BigDecimal bd = new BigDecimal("1");
BigDecimal bg = new BigDecimal("9.0");
eccetera.
Non fai prima con new BigDecimal(8) (con Java 5), altrimenti new BigDecimal(8.0)???
Ma sei andato a vedere i costruttori di BigDecimal???
no scusa, avevo cercato qualche esempio sulla rete
ma
BigDecimal bd = new BigDecimal("8");
BigDecimal bd = new BigDecimal(8);
int i=8;
BigDecimal bd = new BigDecimal(String.valueOf(i));
è la stessa cosa oppure no?
puoi suggerirmi un link per lo studio dei costruttori?
grazie ;)
ma
BigDecimal bd = new BigDecimal("8");
BigDecimal bd = new BigDecimal(8);
int i=8;
BigDecimal bd = new BigDecimal(String.valueOf(i));
è la stessa cosa oppure no?Nel primo caso usi il costruttore di BigDecimal che prende un String, nel secondo caso quello che prende un int (Java 5) e nel terzo caso come il primo solo che prima da un intero ottieni una stringa e poi la passi a BigDecimal (inutile quindi).
puoi suggerirmi un link per lo studio dei costruttori?Sui concetti relativi ai costruttori?? Qualunque buon libro o tutorial online!
http://www.google.it/search?hl=it&q=java+constructors&btnG=Cerca+con+Google&meta=
Per valori interi l'uso di un primitivo (byte, short, int, long) o di una stringa è uguale.
Per valori che hanno una parte frazionaria (float, double) l'uso di un valore double o float, sia esso una variabile piuttosto che un letterale, è problematico perchè BigDecimal assume l'esatto valore del numero che gli passi. Tale valore può non corrispondere a quello espresso dal letterale numerico.
BigDecimal(8.1)
è all'incirca 8.1 mentre
BigDecimal("8.1") è esattamente 8.1.
Considera inoltre l'ovvio rilievo che inizializzare un numero a precisione arbitraria con un valore a precisione limitata è un caso non banale di schizofrenia :D:
Considera inoltre l'ovvio rilievo che inizializzare un numero a precisione arbitraria con un valore a precisione limitata è un caso non banale di schizofrenia :D:
cosa vuol dire inizializzare un numero a precisione arbitraria con un valore a precisione limitata ? mi fai un esempio?
Era una facezia. Ammazza, tutti allegri eh? :D
Un double ha 16 cifre significative. Prendi il numero:
4512014.1482265731234567890
e conta fino a 16, a partire da sinistra (la prima cifra diversa da zero). Quello è il double.
4512014.148226573
il resto
1234567890
è fuffa.
Puoi dire:
double x = 4512014.1482265731234567890;
BigDecimal v = new BigDecimal(x);
ma è "schizofrenico" perchè tra il valore di x e il valore di v non c'è differenza, dal punto di vista della coincidenza tra il letterale scritto (4512014.1482265731234567890) ed il valore assunto. Solo 16 cifre del double sono significative: le altre sai già (perchè hai usato un double) che non saranno rappresentate correttamente.
E' la stringa il pane del BigDecimal. Perchè è coerente con l'intenzione. Io voglio un numero che vale esattamente questo: e vomiti un improperio di cifre. E' ok, BigDecimal serve (anche) a quello: a prescindere dal numero di cifre che scrivi sai già che tutto il numero che hai scritto sarà rappresentato, che ogni cifra sarà correttamente assunta perchè la precisione di un BigDecimal è arbitraria.
Perchè tu hai chiesto di BigDecimal? Be', perchè vuoi che un numero con una quantità arbitraria di cifre sia correttamente rappresentato e double non può farlo. Se mi usi un double per inizializzare il BigDecimal siamo punto e a capo :D.
grazie per la spiegazione!!! :)
ma se faccio 3/2 con i BigDecimal come faccio ad ottenere 1.5
perchè se faccio così arrotonda a 1 oppure a 2:
BigDecimal num1 = new BigDecimal("3");
BigDecimal num2 = new BigDecimal("2");
BigDecimal divisione_1 = num1.divide(num2, BigDecimal.ROUND_UP);
BigDecimal divisione_2 = num1.divide(num2, BigDecimal.ROUND_DOWN);
:help:
Devi specificare quante cifre dopo la virgola vuoi che siano rappresentate. Puoi farlo in costruzione (con la stringa):
BigDecimal num1 = new BigDecimal("3.0"); //1 cifra dopo la virgola
o con il metodo setScale, che restituisce un nuovo BigDecimal:
BigDecimal num1 = new BigDecimal("3").setScale(1); //1 cifra dopo la virgola
import java.math.BigDecimal;
public class Operazioni_2 {
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal("3");
BigDecimal num2 = new BigDecimal("2");
BigDecimal divisione = num1.divide(num2, BigDecimal.ROUND_HALF_EVEN).setScale(3);
// se faccio 10/99 questo crea eccezione
BigDecimal divisione_2 = num1.divide(num2).setScale(3);
System.out.println("risultato divisione = " +divisione);
System.out.println("risultato divisione_2 = " +divisione_2);
}
}
se faccio 3/2 il primo arrotonda a 2.000 e il secondo da 1.500 quindi il secondo è un risultato buono ma il problema sorge se faccio 10/99 dove BigDecimal divisione_2 = num1.divide(num2).setScale(3); non riesce ad approssimare e causa l'eccezione!!! c'è un modo per avere il secondo che crea un approssimazione giusta (tipo quella 3/2=1.500) ma senza eccezione :help:
Per un milione di marenghi d'argento, cosa c'è di diverso tra la divisione che fa a buon fine e la divisione che causa l'eccezione?
BigDecimal divisione = num1.divide(num2, BigDecimal.ROUND_HALF_EVEN).setScale(3);
BigDecimal divisione_2 = num1.divide(num2).setScale(3);
Cinque, quattro, tre, due...
E sento che ti verrà un altro dubbio. Lo sento.
Per un milione di marenghi d'argento, cosa c'è di diverso tra la divisione che fa a buon fine e la divisione che causa l'eccezione?
BigDecimal divisione = num1.divide(num2, BigDecimal.ROUND_HALF_EVEN).setScale(3);
BigDecimal divisione_2 = num1.divide(num2).setScale(3);
Cinque, quattro, tre, due...
E sento che ti verrà un altro dubbio. Lo sento.
PGI-Bis l'unica differenza credo che sia solo nel fatto che in uno chiedo di arrotondare e nell'altro no :D
il mio "dubbio" è come fare ad avere una divisione che non approssima in maniera drastica (cioè 1.5-->1 oppure 1.5-->2) ma che faccia un "arrotondamento giusto" tipo 3/2=1.5 ... senza causarmi eccezioni !!! c'è una soluzione? ho provato anche BigDecimal.ROUND_UP e BigDecimal.ROUND_DOWN
grazie!!! ;)
Non è che tu chieda di arrotondare. Senza specificare il RoundingMode BigDecimal spara un'eccezione se l'operazione che conduce genera un risultato che non è esattamente rappresentabile con il numero di cifre adottate.
Se vuoi evitare di dover specificare le operazioni di arrotondamento ad ogni operazioni puoi usare MathContext nell'inizializzazione del numero.
MathContext ctx = new MathContext(1, RoundingMode.UP);
BigDecimal a = new BigDecimal(..., ctx);
BigDecimal b = new BigDecimal(..., ctx);
BigDecimal c = a.divide(b);
Prova ad esempio:
BigDecimal num1 = new BigDecimal ("2");
BigDecimal num2 = new BigDecimal ("3");
System.out.println (num1.setScale (3).divide (num2, BigDecimal.ROUND_DOWN));
grazie a entrambi per la risposta, ho provato tutti e due
import java.math.BigDecimal;
import java.math.*;
public class Operazioni_2 {
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal ("15");
BigDecimal num2 = new BigDecimal ("7");
System.out.println (num1.setScale (10).divide (num2, BigDecimal.ROUND_DOWN));
MathContext ctx = new MathContext(1, RoundingMode.UP);
BigDecimal a = new BigDecimal("15", ctx);
BigDecimal b = new BigDecimal("7", ctx);
BigDecimal c = a.divide(b);
System.out.println (c);
}
}
questo è il risultato:
2.1428571428
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1513)
at Operazioni_2.main(Operazioni_2.java:15)
MathContext ctx = new MathContext(1, RoundingMode.UP);
BigDecimal a = new BigDecimal("15", ctx);
BigDecimal b = new BigDecimal("7", ctx);
BigDecimal c = a.divide(b);
System.out.println (c);Il MathContext specificato nel costruttore serve solo per la inizializzazione del BigDecimal, non per fare altre operazioni sul numero!
Quindi se vuoi usare un MathContext, applicalo alla divisione:
BigDecimal c = a.divide(b, ctx);
E' colpa mia, ho dimenticato un ctx nell'esempio.
grazie ho provato!!! ctx serve per fissare il numero di cifre dopo la virgola eseguendo un arrotondamento se queste superano il numero specificato in MathContext ctx = new MathContext(10, RoundingMode.UP); ? ho capito bene?
import java.math.BigDecimal;
import java.math.*;
public class Operazioni_2 {
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal ("7");
BigDecimal num2 = new BigDecimal ("8");
System.out.println (num1.setScale (10).divide (num2, BigDecimal.ROUND_DOWN));
MathContext ctx = new MathContext(10, RoundingMode.UP);
BigDecimal a = new BigDecimal("7");
BigDecimal b = new BigDecimal("8");
//BigDecimal c = a.divide(b);
BigDecimal c = a.divide(b, ctx); // <--------
System.out.println (c);
}
}
nell'esempio ho tolto ctx da
BigDecimal a = new BigDecimal("7", ctx);
BigDecimal b = new BigDecimal("8", ctx);
lasciandolo solo in BigDecimal c = a.divide(b, ctx);
è corretto, cioè posso fare anche così oppure potrebbe modifica il risultato?
grazie!!!
In effetti il MathContext in costruzione non serve a niente se il valore è una stringa nota, perchè in quel caso non è possibile che ci siano "decimali pazzi" (float o double) o che il valore sia espresso con una precisione eccessiva.
Se invece i valori arrivano da fonti non controllate allora è meglio specificare il grado di precisione con cui saranno fatti i conti e, dunque, quanti decimali tra quelli eventualmente presenti saranno effettivamente considerati.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.