Domanda

public class doublePrecision {
    public static void main(String[] args) {

        double total = 0;
        total += 5.6;
        total += 5.8;
        System.out.println(total);
    }
}

Viene stampato il codice sopra:

11.399999999999

Come potrei ottenere questo per stampare (o essere in grado di usarlo come) 11.4?

È stato utile?

Soluzione

Come altri hanno già detto, probabilmente vorrai usare BigDecimal , se vuoi avere una rappresentazione esatta dell'11.4.

Ora, una piccola spiegazione del perché ciò sta accadendo:

I tipi di primitive float e double in Java sono virgola mobile , dove il numero è memorizzato come rappresentazione binaria di una frazione e di un esponente.

Più specificamente, un valore in virgola mobile a precisione doppia come il tipo byte è un valore a 64 bit, dove:

  • 1 bit indica il segno (positivo o negativo).
  • 11 bit per l'esponente.
  • 52 bit per le cifre significative (la parte frazionaria come binaria).

Queste parti sono combinate per produrre una char rappresentazione di un valore.

(Fonte: Wikipedia: doppia precisione )

Per una descrizione dettagliata di come vengono gestiti i valori in virgola mobile in Java, consultare Sezione 4.2.3: Tipi, formati e valori in virgola mobile delle specifiche del linguaggio Java.

I tipi int, long, 11.399999999999, 5.6 + 5.8 sono fixed- punto , che sono rappresentazioni esatte di numeri. A differenza dei numeri in virgola fissa, alcuni numeri in virgola mobile alcune volte (sicuro supporre & Quot; il più delle volte & Quot;) non saranno in grado di restituire una rappresentazione esatta di un numero. Questo è il motivo per cui finisci con <=> come risultato di <=>.

Quando si richiede un valore esatto, ad esempio 1.5 o 150.1005, è necessario utilizzare uno dei tipi a virgola fissa, che sarà in grado di rappresentare esattamente il numero.

Come già accennato più volte, Java ha un <=> classe che gestirà numeri molto grandi e numeri molto piccoli.

Dal riferimento API Java per la classe <=>:

  

Immutabile,   decimale con segno di precisione arbitraria   numeri. Un BigDecimal è costituito da un   intero di precisione arbitraria non scalato   valore e scala intera a 32 bit. Se   zero o positivo, la scala è il   numero di cifre a destra del   punto decimale. Se negativo, il   il valore non scalato del numero è   moltiplicato per dieci al potere del   negazione della scala. Il valore di   il numero rappresentato dal   BigDecimal è quindi (unscaledValue   & # 215; 10 ^ -scale).

Ci sono state molte domande su Stack Overflow relative alla questione dei numeri in virgola mobile e alla sua precisione. Ecco un elenco di domande correlate che potrebbero essere di interesse:

Se vuoi davvero scendere ai dettagli grintosi dei numeri in virgola mobile, dai un'occhiata a Ciò che ogni scienziato informatico dovrebbe sapere sull'aritmetica a virgola mobile .

Altri suggerimenti

Quando inserisci un doppio numero, ad esempio 33.33333333333333, il valore che ottieni è in realtà il valore di doppia precisione rappresentabile più vicino, che è esattamente:

33.3333333333333285963817615993320941925048828125

Dividendolo per 100 si ottiene:

0.333333333333333285963817615993320941925048828125

che inoltre non è rappresentabile come un numero a doppia precisione, quindi di nuovo viene arrotondato al valore rappresentabile più vicino, che è esattamente:

0.3333333333333332593184650249895639717578887939453125

Quando si stampa questo valore, viene arrotondato ancora una volta a 17 cifre decimali, fornendo:

0.33333333333333326

Se vuoi solo elaborare valori come frazioni, puoi creare una classe di frazioni che contiene un campo numeratore e denominatore.

Scrivi metodi per aggiungere, sottrarre, moltiplicare e dividere e un metodo toDouble. In questo modo puoi evitare i float durante i calcoli.

EDIT: implementazione rapida,

public class Fraction {

private int numerator;
private int denominator;

public Fraction(int n, int d){
    numerator = n;
    denominator = d;
}

public double toDouble(){
    return ((double)numerator)/((double)denominator);
}


public static Fraction add(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop + bTop, a.denominator * b.denominator);
    }
    else{
        return new Fraction(a.numerator + b.numerator, a.denominator);
    }
}

public static Fraction divide(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.denominator, a.denominator * b.numerator);
}

public static Fraction multiply(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.numerator, a.denominator * b.denominator);
}

public static Fraction subtract(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop-bTop, a.denominator*b.denominator);
    }
    else{
        return new Fraction(a.numerator - b.numerator, a.denominator);
    }
}

}

Osserva che avresti avuto lo stesso problema se avessi usato l'aritmetica decimale a precisione limitata e volessi trattare 1/3: 0.333333333 * 3 è 0.999999999, non 1.00000000.

Sfortunatamente, 5.6, 5.8 e 11.4 non sono semplicemente numeri rotondi in binario, perché coinvolgono le quinte. Quindi la loro rappresentazione float non è esatta, proprio come 0.3333 non è esattamente 1/3.

Se tutti i numeri che usi sono decimali non ricorrenti e desideri risultati esatti, usa BigDecimal. O come altri hanno già detto, se i tuoi valori sono come il denaro nel senso che sono tutti un multiplo di 0,01 o 0,001 o qualcosa del genere, allora moltiplica tutto per una potenza fissa di 10 e usa int o long (addizione e sottrazione sono banale: attenzione alla moltiplicazione).

Tuttavia, se sei soddisfatto del binario per il calcolo, ma vuoi solo stampare le cose in un formato leggermente più amichevole, prova java.util.Formatter o String.format. Nella stringa di formato specificare una precisione inferiore alla precisione completa di un doppio. A 10 cifre significative, diciamo, 11.399999999999 è 11.4, quindi il risultato sarà quasi altrettanto accurato e più leggibile dall'uomo nei casi in cui il risultato binario è molto vicino a un valore che richiede solo poche cifre decimali.

La precisione da specificare dipende un po 'da quanti calcoli hai fatto con i tuoi numeri: in generale più fai, più si accumulano errori, ma alcuni algoritmi lo accumulano molto più velocemente di altri (sono chiamati < !> quot; unstable " al contrario di " stable " rispetto agli errori di arrotondamento). Se tutto ciò che stai facendo è aggiungere alcuni valori, indovinerei che l'eliminazione di un solo decimale di precisione risolverà le cose. Esperimento.

Potresti voler esaminare l'uso della classe java.math.BigDecimal di java se hai davvero bisogno di matematica di precisione. Ecco un buon articolo di Oracle / Sun su il caso di BigDecimal . Mentre non puoi mai rappresentare 1/3 come qualcuno ha menzionato, puoi avere il potere di decidere esattamente quanto vuoi che il risultato sia preciso. setScale () è tuo amico .. :)

Ok, perché al momento ho troppo tempo a disposizione, ecco un esempio di codice relativo alla tua domanda:

import java.math.BigDecimal;
/**
 * Created by a wonderful programmer known as:
 * Vincent Stoessel
 * xaymaca@gmail.com
 * on Mar 17, 2010 at  11:05:16 PM
 */
public class BigUp {

    public static void main(String[] args) {
        BigDecimal first, second, result ;
        first = new BigDecimal("33.33333333333333")  ;
        second = new BigDecimal("100") ;
        result = first.divide(second);
        System.out.println("result is " + result);
       //will print : result is 0.3333333333333333


    }
}

e per collegare la mia nuova lingua preferita, Groovy, ecco un esempio più preciso della stessa cosa:

import java.math.BigDecimal

def  first =   new BigDecimal("33.33333333333333")
def second = new BigDecimal("100")


println "result is " + first/second   // will print: result is 0.33333333333333

Abbastanza sicuro che avresti potuto trasformarlo in un esempio di tre righe. :)

Se vuoi la precisione esatta, usa BigDecimal. Altrimenti, puoi usare ints moltiplicati per 10 ^ qualunque precisione tu voglia.

Come altri hanno notato, non tutti i valori decimali possono essere rappresentati come binari poiché i decimali si basano su potenze di 10 e i binari si basano su potenze di due.

Se la precisione conta, usa BigDecimal, ma se vuoi solo un output amichevole:

System.out.printf("%.2f\n", total);

Ti darà:

11.40

Stai andando contro la limitazione di precisione del tipo double.

Java.Math ha alcune funzionalità aritmetiche di precisione arbitraria.

Non puoi, perché 7.3 non ha una rappresentazione finita in binario. Il più vicino che puoi ottenere è 2054767329987789/2 ** 48 = 7.3 + 1/1407374883553280.

Dai un'occhiata a http://docs.python.org/tutorial/floatingpoint.html per un'ulteriore spiegazione. (È sul sito Web di Python, ma Java e C ++ hanno lo stesso & Quot; problema & Quot ;.)

La soluzione dipende da quale sia esattamente il tuo problema:

  • Se è che non ti piace vedere tutte quelle cifre del rumore, correggi la formattazione della stringa. Non visualizzare più di 15 cifre significative (o 7 per float).
  • Se l'inesattezza dei tuoi numeri sta rompendo cose come " if " , quindi dovresti scrivere if (abs (x - 7.3) < TOLLERANZA) invece di if (x == 7.3).
  • Se lavori con i soldi, probabilmente quello che vuoi davvero è un punto fisso decimale. Memorizza un numero intero di centesimi o qualunque sia l'unità più piccola della tua valuta.
  • (MOLTO NON PIACEVOLE) Se hai bisogno di più di 53 bit significativi (15-16 cifre significative) di precisione, usa un tipo a virgola mobile ad alta precisione, come BigDecimal.
private void getRound() {
    // this is very simple and interesting 
    double a = 5, b = 3, c;
    c = a / b;
    System.out.println(" round  val is " + c);

    //  round  val is  :  1.6666666666666667
    // if you want to only two precision point with double we 
            //  can use formate option in String 
           // which takes 2 parameters one is formte specifier which 
           // shows dicimal places another double value 
    String s = String.format("%.2f", c);
    double val = Double.parseDouble(s);
    System.out.println(" val is :" + val);
    // now out put will be : val is :1.67
}

Usa java.math.BigDecimal

I doppi sono frazioni binarie internamente, quindi a volte non possono rappresentare le frazioni decimali con l'esatto decimale.

Moltiplica tutto per 100 e conservalo in centesimi.

I computer memorizzano i numeri in binario e in realtà non possono rappresentare esattamente numeri come 33.333333333 o 100.0. Questa è una delle cose difficili dell'uso dei doppi. Dovrai semplicemente arrotondare la risposta prima di mostrarla a un utente. Fortunatamente nella maggior parte delle applicazioni, in ogni caso non sono necessarie molte cifre decimali.

I numeri in virgola mobile differiscono dai numeri reali in quanto per ogni dato numero in virgola mobile c'è un successivo numero in virgola mobile più alto. Come gli interi. Non esiste un numero intero compreso tra 1 e 2.

Non c'è modo di rappresentare 1/3 come float. C'è un galleggiante sotto di esso e c'è un galleggiante sopra di esso, e c'è una certa distanza tra di loro. E 1/3 è in quello spazio.

Apfloat per Java afferma di funzionare con numeri a virgola mobile di precisione arbitraria, ma non l'ho mai usato. Probabilmente vale la pena dare un'occhiata. http://www.apfloat.org/apfloat_java/

Una domanda simile è stata posta qui prima Libreria Java ad alta precisione in virgola mobile

I doppi sono approssimazioni dei numeri decimali nella sorgente Java. Stai vedendo la conseguenza della mancata corrispondenza tra il doppio (che è un valore con codice binario) e la tua fonte (che è con codice decimale).

Java sta producendo l'approssimazione binaria più vicina. Puoi usare java.text.DecimalFormat per visualizzare un valore decimale più bello.

Usa un BigDecimal. Ti consente anche di specificare le regole di arrotondamento (come ROUND_HALF_EVEN, che ridurrà al minimo l'errore statistico arrotondando al vicino pari se entrambi hanno la stessa distanza; cioè sia 1,5 che 2,5 arrotondati a 2).

Dai un'occhiata a BigDecimal, gestisce i problemi relativi all'aritmetica in virgola mobile del genere.

La nuova chiamata sarebbe simile a questa:

term[number].coefficient.add(co);

Utilizzare setScale () per impostare il numero di precisione del decimale da utilizzare.

Perché non usare il metodo round () della classe Math?

// The number of 0s determines how many digits you want after the floating point
// (here one digit)
total = (double)Math.round(total * 10) / 10;
System.out.println(total); // prints 11.4

Se non hai altra scelta che usare i doppi valori, puoi usare il codice qui sotto.

public static double sumDouble(double value1, double value2) {
    double sum = 0.0;
    String value1Str = Double.toString(value1);
    int decimalIndex = value1Str.indexOf(".");
    int value1Precision = 0;
    if (decimalIndex != -1) {
        value1Precision = (value1Str.length() - 1) - decimalIndex;
    }

    String value2Str = Double.toString(value2);
    decimalIndex = value2Str.indexOf(".");
    int value2Precision = 0;
    if (decimalIndex != -1) {
        value2Precision = (value2Str.length() - 1) - decimalIndex;
    }

    int maxPrecision = value1Precision > value2Precision ? value1Precision : value2Precision;
    sum = value1 + value2;
    String s = String.format("%." + maxPrecision + "f", sum);
    sum = Double.parseDouble(s);
    return sum;
}

Non sprecare il tuo efford usando BigDecimal. Nel 99,99999% dei casi non è necessario. Il tipo java doppio è approssimativo, ma in quasi tutti i casi è sufficientemente preciso. Tieni presente che hai un errore alla 14a cifra significativa. Questo è davvero trascurabile!

Per ottenere un buon output usa:

System.out.printf("%.2f\n", total);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top