Come formattare correttamente i numeri fluttuanti su String senza 0 decimali non necessari?


496

Un doppio a 64 bit può rappresentare esattamente il numero intero +/- 2 53

Dato questo fatto, ho scelto di usare un doppio tipo come singolo tipo per tutti i miei tipi, poiché il mio intero più grande è senza segno a 32 bit.

Ma ora devo stampare questi pseudo numeri interi, ma il problema è che sono anche mescolati con doppi reali.

Quindi, come faccio a stampare questi doppi bene in Java?

Ho provato String.format("%f", value), che è vicino, tranne per il fatto che ottengo molti zeri finali per piccoli valori.

Ecco un esempio di output di %f

232.00000000
0,18 miliardi
1237875192,0
4,5800 milioni
0.00000000
1.23450000

Quello che voglio è:

232
0,18
1237875192
4.58
0
1.2345

Certo che posso scrivere una funzione per tagliare quegli zeri, ma questo è un sacco di perdita di prestazioni dovuta alla manipolazione delle stringhe. Posso fare di meglio con un altro codice di formato?

MODIFICARE

Le risposte di Tom E. e Jeremy S. sono inaccettabili in quanto entrambe arbitrariamente arrotondano al secondo decimale. Si prega di comprendere il problema prima di rispondere.

MODIFICA 2

Si noti che String.format(format, args...)dipende dalle impostazioni locali (vedere le risposte di seguito).


Se tutto ciò che vuoi sono numeri interi, perché non usare un long? Ottieni più botto a 2 ^ 63-1, nessuna formattazione scomoda e prestazioni migliori.
basszero,

14
Perché alcuni valori sono in realtà raddoppiati
Pirolistico

2
Alcuni casi in cui questo problema si è verificato è stato un bug nel JDK 7: stackoverflow.com/questions/7564525/...
Pyrolistical

Sono solo io o JavaScript è migliore al 100% nella conversione da numero a stringa di Java?
Andy,

System.out.println("YOUR STRING" + YOUR_DOUBLE_VARIABLE);
Shayan Amani,

Risposte:


399

Se l'idea è di stampare numeri interi memorizzati come doppi come se fossero numeri interi, e altrimenti stampare i doppi con la precisione minima necessaria:

public static String fmt(double d)
{
    if(d == (long) d)
        return String.format("%d",(long)d);
    else
        return String.format("%s",d);
}

produce:

232
0.18
1237875192
4.58
0
1.2345

E non si basa sulla manipolazione di stringhe.


9
D'accordo, questa è una cattiva risposta, non usarla. Non funziona con un valore doublesuperiore al intvalore massimo . Anche con longesso fallirebbe comunque per numeri enormi. Inoltre restituirà una stringa in forma esponenziale, ad esempio "1.0E10", per valori di grandi dimensioni, che probabilmente non è ciò che desidera il richiedente. Utilizzare al %fposto della %sseconda stringa di formato per risolvere il problema.
jlh

26
L'OP ha dichiarato esplicitamente di non voler formattare l'output utilizzando %f. La risposta è specifica della situazione descritta e dell'output desiderato. L'OP ha suggerito che il loro valore massimo era un int senza segno a 32 bit, che ho assunto per significare che intera accettabile (il segno non in realtà esiste in Java e nessun esempio era problematico), ma cambiare intin longè una soluzione banale se la situazione è diversa.
JasonD,

1
Dove nella domanda dice che non dovrebbe farlo?
JasonD,

6
String.format("%s",d)??? Parla di spese generali non necessarie. Usa Double.toString(d). Lo stesso vale per l'altro: Long.toString((long)d).
Andreas,

15
Il problema è che %snon funziona con Locales. In tedesco, usiamo un "," invece di un "." in cifre decimali. Mentre String.format(Locale.GERMAN, "%f", 1.5)restituisce "1.500000", String.format(Locale.GERMAN, "%s", 1.5)restituisce "1.5" - con un ".", Che è falso in lingua tedesca. Esiste anche una versione locale di "% s"?
Felix Edelmann,

414
new DecimalFormat("#.##").format(1.199); //"1.2"

Come indicato nei commenti, questa non è la risposta giusta alla domanda originale.
Detto questo, è un modo molto utile per formattare i numeri senza zeri finali inutili.


16
Una nota importante qui è che 1.1 verrebbe correttamente formattato come "1.1" senza zeri finali.
Steve Pomeroy,

53
E se ti capita di desiderare un numero specifico di zero finali (ad esempio se stai stampando importi in denaro), puoi usare '0' invece di '#' (ovvero nuovo formato DecimalFormat ("0.00").) (Importo);) questo non è ciò che OP voleva, ma può essere utile come riferimento.
TJ Ellis,

22
Sì, in quanto autore originale della domanda questa è la risposta ERRATA. Divertente quanti voti alti ci sono. Il problema con questa soluzione è che si arrotondano arbitrariamente a 2 decimali.
Pirolistico il

11
@Mazyod perché puoi sempre passare un puntamento mobile con più decimali rispetto al formato. Questo è scrivere codice che funzionerà per la maggior parte del tempo ma non copre tutti i casi limite.
Pirolistico

15
@Pyrolistical - IMHO, ci sono molti voti positivi perché, sebbene questa sia la soluzione sbagliata per te, è la soluzione giusta per il 99% + di coloro che trovano questo Q&A: di solito, le ultime cifre di un doppio sono "noise", che ingombra l'output, interferendo con la leggibilità. Quindi il programmatore determina quante cifre sono utili per la persona che legge l'output e ne specifica quante. Una situazione comune è che si sono accumulati piccoli errori matematici, quindi un valore potrebbe essere 12.000000034, ma preferirei arrotondare a 12 e visualizzarlo in modo compatto come "12". E "12.340000056" => "12.34".
ToolmakerSteve

226
String.format("%.2f", value) ;

13
È corretto, ma stampa sempre zeri finali anche se non c'è parte frazionaria. String.format ("%. 2f, 1.0005) stampa 1.00 e non 1. Esiste un identificatore di formato per non stampare la parte frazionaria se non esiste?
Emre Yazici,

87
Giù votato poiché la domanda chiede di eliminare tutti gli zeri finali e questa risposta lascerà sempre due punti fluttuanti indipendentemente dal fatto che sia zero.
Zulaxia,

Il DecimalFormat è stato un bel trucco - anche se ho finito per usarlo per la mia situazione (timer a livello di gioco) dato che gli zeri finali sembravano migliori.
Timothy Lee Russell,

2
Penso che tu possa gestire correttamente gli zeri finali usando g invece di f.
Peter Ajtai,

3
Ho usato questa soluzione in un sistema di produzione con "% .5f", ed è davvero pessima, non usarla ... perché ha stampato questo: 5.12E-4 invece di 0.000512
hamish

87

In breve:

Se vuoi sbarazzarti degli zeri finali e dei problemi di Locale, dovresti usare:

double myValue = 0.00000021d;

DecimalFormat df = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
df.setMaximumFractionDigits(340); //340 = DecimalFormat.DOUBLE_FRACTION_DIGITS

System.out.println(df.format(myValue)); //output: 0.00000021

Spiegazione:

Perché altre risposte non mi andavano bene:

  • Double.toString()o System.out.printlno FloatingDecimal.toJavaFormatStringusa notazioni scientifiche se double è inferiore a 10 ^ -3 o maggiore o uguale a 10 ^ 7

    double myValue = 0.00000021d;
    String.format("%s", myvalue); //output: 2.1E-7
  • utilizzando %f, la precisione decimale predefinita è 6, altrimenti è possibile codificarla, ma si ottengono ulteriori zeri se si hanno meno decimali. Esempio :

    double myValue = 0.00000021d;
    String.format("%.12f", myvalue); //output: 0.000000210000
  • usando setMaximumFractionDigits(0);o %.0frimuovendo qualsiasi precisione decimale, che va bene per interi / long ma non per double

    double myValue = 0.00000021d;
    System.out.println(String.format("%.0f", myvalue)); //output: 0
    DecimalFormat df = new DecimalFormat("0");
    System.out.println(df.format(myValue)); //output: 0
  • usando DecimalFormat, sei dipendente locale. Nelle impostazioni locali francesi, il separatore decimale è una virgola, non un punto:

    double myValue = 0.00000021d;
    DecimalFormat df = new DecimalFormat("0");
    df.setMaximumFractionDigits(340);
    System.out.println(df.format(myvalue));//output: 0,00000021

    L'uso della localizzazione INGLESE ti assicura di ottenere un punto per il separatore decimale, ovunque il tuo programma verrà eseguito

Perché usare 340 allora per setMaximumFractionDigits?

Due motivi:

  • setMaximumFractionDigitsaccetta un numero intero ma la sua implementazione ha un numero massimo consentito di DecimalFormat.DOUBLE_FRACTION_DIGITScui uguale a 340
  • Double.MIN_VALUE = 4.9E-324 quindi con 340 cifre sei sicuro di non arrotondare la precisione doppia e libera

Questo non funziona per numeri interi, ad es. "2" diventa "2."
Kap

Grazie, ho risolto la risposta utilizzando lo schema 0anziché#.
JBE

Non stai usando la costante DecimalFormat.DOUBLE_FRACTION_DIGITSma stai usando il valore 340, per il quale fornisci un commento per mostrare che è uguale DecimalFormat.DOUBLE_FRACTION_DIGITS. Perché non usare semplicemente la costante ???
Maarten Bodewes,

1
Poiché questo attributo non è pubblico ... è "pacchetto friendly"
JBE

4
Grazie! In realtà, questa risposta è l'unica che soddisfa davvero tutti i requisiti menzionati nella domanda: non mostra zeri non necessari, non arrotonda i numeri e dipende dalle impostazioni locali. Grande!
Felix Edelmann,

26

Perchè no:

if (d % 1.0 != 0)
    return String.format("%s", d);
else
    return String.format("%.0f",d);

Questo dovrebbe funzionare con i valori estremi supportati da Double. I rendimenti:

0.12
12
12.144252
0

2
Preferisco questa risposta con la quale non è necessario eseguire la conversione del tipo.
Jeff T.

Breve spiegazione: "%s"sostanzialmente chiama d.toString()ma non funziona con into se d==null!
Neph,

24

Sulla mia macchina, la seguente funzione è circa 7 volte più veloce della funzione fornita dalla risposta di JasonD , poiché evita String.format:

public static String prettyPrint(double d) {
  int i = (int) d;
  return d == i ? String.valueOf(i) : String.valueOf(d);
}

1
Hmm, questo non tiene conto delle localizzazioni, ma nemmeno di JasonD.
TWiStErRob,

22

I miei 2 centesimi:

if(n % 1 == 0) {
    return String.format(Locale.US, "%.0f", n));
} else {
    return String.format(Locale.US, "%.1f", n));
}

2
O semplicemente return String.format(Locale.US, (n % 1 == 0 ? "%.0f" : "%.1f"), n);.
MC Emperor

fallito quando 23.00123 ==> 23.00
aswzen

11

No, non importa.

La perdita di prestazioni dovuta alla manipolazione delle stringhe è zero.

Ed ecco il codice per tagliare la fine dopo %f

private static String trimTrailingZeros(String number) {
    if(!number.contains(".")) {
        return number;
    }

    return number.replaceAll("\\.?0*$", "");
}

7
Ho effettuato il downgrade perché la tua soluzione non è il modo migliore per procedere. Dai un'occhiata a String.format. È necessario utilizzare il tipo di formato corretto, float in questa istanza. Guarda la mia risposta sopra.
jjnguy,

6
Ho votato perché ho lo stesso problema e nessuno qui sembra capire il problema.
Obay

1
Sottovalutato come il formato decimale menzionato nel post di Tom è esattamente quello che stavi cercando. Elimina gli zeri in modo abbastanza efficace.
Steve Pomeroy,

4
Quanto sopra, forse vuole tagliare gli zeri SENZA arrotondamento? PS @Pyrolistical, sicuramente puoi semplicemente usare number.replaceAll (".? 0 * $", ""); (dopo contiene (".") ovviamente)
Rehno Lindeque,

1
Ok, allora come saresti in grado di raggiungere il mio obiettivo con DecimalFormat?
Pirolistico il

8

Usa un DecimalFormatesetMinimumFractionDigits(0)


Vorrei aggiungere setMaximumFractionDigits(2)e setGroupingUsed(false)(OP non lo menziona ma dall'esempio sembra che sia necessario). Inoltre, un piccolo caso di test non fa male poiché è banale in questo caso. Tuttavia, poiché penso che sia la soluzione più semplice, un voto è un voto :)
acrespo

6
if (d == Math.floor(d)) {
    return String.format("%.0f", d);
} else {
    return Double.toString(d);
}

1
Penso che ti seguirò in questo: D
codice aereo

5

Si prega di notare che String.format(format, args...)è dipendente dal luogo perché formatta utilizzando impostazioni internazionali predefinite dell'utente, che è, probabilmente con le virgole e gli spazi anche all'interno come 123 456.789 o 123,456.789 , che può essere non è esattamente quello che ci si aspetta.

Puoi preferire l'uso String.format((Locale)null, format, args...).

Per esempio,

    double f = 123456.789d;
    System.out.println(String.format(Locale.FRANCE,"%f",f));
    System.out.println(String.format(Locale.GERMANY,"%f",f));
    System.out.println(String.format(Locale.US,"%f",f));

stampe

123456,789000
123456,789000
123456.789000

e questo è ciò che String.format(format, args...)farà in diversi paesi.

EDIT Ok, dato che c'è stata una discussione sulle formalità:

    res += stripFpZeroes(String.format((Locale) null, (nDigits!=0 ? "%."+nDigits+"f" : "%f"), value));
    ...

protected static String stripFpZeroes(String fpnumber) {
    int n = fpnumber.indexOf('.');
    if (n == -1) {
        return fpnumber;
    }
    if (n < 2) {
        n = 2;
    }
    String s = fpnumber;
    while (s.length() > n && s.endsWith("0")) {
        s = s.substring(0, s.length()-1);
    }
    return s;
}

1
dovresti aggiungere questo come commento alla risposta accettata
Pirolistico

I commenti non consentono la lunghezza o il formato di questo addendum. Dal momento che aggiunge informazioni forse utili, penso che dovrebbe essere consentito così come è piuttosto che eliminato.
Terry Jan Reedy,

5

Ho fatto un DoubleFormatterper convertire in modo efficiente un gran numero di doppi valori in una stringa piacevole / presentabile:

double horribleNumber = 3598945.141658554548844; 
DoubleFormatter df = new DoubleFormatter(4,6); //4 = MaxInteger, 6 = MaxDecimal
String beautyDisplay = df.format(horribleNumber);
  • Se la parte intera di V ha più di MaxInteger => visualizza V in formato scienziato (1.2345e + 30), altrimenti visualizza in formato normale 124.45678.
  • il MaxDecimal decide i numeri delle cifre decimali (tagliare con arrotondamento del banco)

Ecco il codice:

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;

/**
 * Convert a double to a beautiful String (US-local):
 * 
 * double horribleNumber = 3598945.141658554548844; 
 * DoubleFormatter df = new DoubleFormatter(4,6);
 * String beautyDisplay = df.format(horribleNumber);
 * String beautyLabel = df.formatHtml(horribleNumber);
 * 
 * Manipulate 3 instances of NumberFormat to efficiently format a great number of double values.
 * (avoid to create an object NumberFormat each call of format()).
 * 
 * 3 instances of NumberFormat will be reused to format a value v:
 * 
 * if v < EXP_DOWN, uses nfBelow
 * if EXP_DOWN <= v <= EXP_UP, uses nfNormal
 * if EXP_UP < v, uses nfAbove
 * 
 * nfBelow, nfNormal and nfAbove will be generated base on the precision_ parameter.
 * 
 * @author: DUONG Phu-Hiep
 */
public class DoubleFormatter
{
    private static final double EXP_DOWN = 1.e-3;
    private double EXP_UP; // always = 10^maxInteger
    private int maxInteger_;
    private int maxFraction_;
    private NumberFormat nfBelow_; 
    private NumberFormat nfNormal_;
    private NumberFormat nfAbove_;

    private enum NumberFormatKind {Below, Normal, Above}

    public DoubleFormatter(int maxInteger, int maxFraction){
        setPrecision(maxInteger, maxFraction);
    }

    public void setPrecision(int maxInteger, int maxFraction){
        Preconditions.checkArgument(maxFraction>=0);
        Preconditions.checkArgument(maxInteger>0 && maxInteger<17);

        if (maxFraction == maxFraction_ && maxInteger_ == maxInteger) {
            return;
        }

        maxFraction_ = maxFraction;
        maxInteger_ = maxInteger;
        EXP_UP =  Math.pow(10, maxInteger);
        nfBelow_ = createNumberFormat(NumberFormatKind.Below);
        nfNormal_ = createNumberFormat(NumberFormatKind.Normal);
        nfAbove_ = createNumberFormat(NumberFormatKind.Above);
    }

    private NumberFormat createNumberFormat(NumberFormatKind kind) {
        final String sharpByPrecision = Strings.repeat("#", maxFraction_); //if you do not use Guava library, replace with createSharp(precision);
        NumberFormat f = NumberFormat.getInstance(Locale.US);

        //Apply banker's rounding:  this is the rounding mode that statistically minimizes cumulative error when applied repeatedly over a sequence of calculations
        f.setRoundingMode(RoundingMode.HALF_EVEN);

        if (f instanceof DecimalFormat) {
            DecimalFormat df = (DecimalFormat) f;
            DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();

            //set group separator to space instead of comma

            //dfs.setGroupingSeparator(' ');

            //set Exponent symbol to minus 'e' instead of 'E'
            if (kind == NumberFormatKind.Above) {
                dfs.setExponentSeparator("e+"); //force to display the positive sign in the exponent part
            } else {
                dfs.setExponentSeparator("e");
            }

            df.setDecimalFormatSymbols(dfs);

            //use exponent format if v is out side of [EXP_DOWN,EXP_UP]

            if (kind == NumberFormatKind.Normal) {
                if (maxFraction_ == 0) {
                    df.applyPattern("#,##0");
                } else {
                    df.applyPattern("#,##0."+sharpByPrecision);
                }
            } else {
                if (maxFraction_ == 0) {
                    df.applyPattern("0E0");
                } else {
                    df.applyPattern("0."+sharpByPrecision+"E0");
                }
            }
        }
        return f;
    } 

    public String format(double v) {
        if (Double.isNaN(v)) {
            return "-";
        }
        if (v==0) {
            return "0"; 
        }
        final double absv = Math.abs(v);

        if (absv<EXP_DOWN) {
            return nfBelow_.format(v);
        }

        if (absv>EXP_UP) {
            return nfAbove_.format(v);
        }

        return nfNormal_.format(v);
    }

    /**
     * format and higlight the important part (integer part & exponent part) 
     */
    public String formatHtml(double v) {
        if (Double.isNaN(v)) {
            return "-";
        }
        return htmlize(format(v));
    }

    /**
     * This is the base alogrithm: create a instance of NumberFormat for the value, then format it. It should
     * not be used to format a great numbers of value 
     * 
     * We will never use this methode, it is here only to understanding the Algo principal:
     * 
     * format v to string. precision_ is numbers of digits after decimal. 
     * if EXP_DOWN <= abs(v) <= EXP_UP, display the normal format: 124.45678
     * otherwise display scientist format with: 1.2345e+30 
     * 
     * pre-condition: precision >= 1
     */
    @Deprecated
    public String formatInefficient(double v) {

        final String sharpByPrecision = Strings.repeat("#", maxFraction_); //if you do not use Guava library, replace with createSharp(precision);

        final double absv = Math.abs(v);

        NumberFormat f = NumberFormat.getInstance(Locale.US);

        //Apply banker's rounding:  this is the rounding mode that statistically minimizes cumulative error when applied repeatedly over a sequence of calculations
        f.setRoundingMode(RoundingMode.HALF_EVEN);

        if (f instanceof DecimalFormat) {
            DecimalFormat df = (DecimalFormat) f;
            DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();

            //set group separator to space instead of comma

            dfs.setGroupingSeparator(' ');

            //set Exponent symbol to minus 'e' instead of 'E'

            if (absv>EXP_UP) {
                dfs.setExponentSeparator("e+"); //force to display the positive sign in the exponent part
            } else {
                dfs.setExponentSeparator("e");
            }
            df.setDecimalFormatSymbols(dfs);

            //use exponent format if v is out side of [EXP_DOWN,EXP_UP]

            if (absv<EXP_DOWN || absv>EXP_UP) {
                df.applyPattern("0."+sharpByPrecision+"E0");
            } else {
                df.applyPattern("#,##0."+sharpByPrecision);
            }
        }
        return f.format(v);
    }

    /**
     * Convert "3.1416e+12" to "<b>3</b>.1416e<b>+12</b>"
     * It is a html format of a number which highlight the integer and exponent part
     */
    private static String htmlize(String s) {
        StringBuilder resu = new StringBuilder("<b>");
        int p1 = s.indexOf('.');

        if (p1>0) {
            resu.append(s.substring(0, p1));
            resu.append("</b>");
        } else {
            p1 = 0;
        }

        int p2 = s.lastIndexOf('e');
        if (p2>0) {
            resu.append(s.substring(p1, p2));
            resu.append("<b>");
            resu.append(s.substring(p2, s.length()));
            resu.append("</b>");
        } else {
            resu.append(s.substring(p1, s.length()));
            if (p1==0){
                resu.append("</b>");
            }
        }
        return resu.toString();
    }
}

Nota: ho usato 2 funzioni dalla libreria GUAVA. Se non usi GUAVA, codificalo tu stesso:

/**
 * Equivalent to Strings.repeat("#", n) of the Guava library: 
 */
private static String createSharp(int n) {
    StringBuilder sb = new StringBuilder(); 
    for (int i=0;i<n;i++) {
        sb.append('#');
    }
    return sb.toString();
}

1
Se conosci la precisione, usa un BigDecimal. Vedi docs.oracle.com/javase/1.5.0/docs/api/java/math/…
Pyrolistical

5

Questo farà il lavoro bene, so che l'argomento è vecchio, ma stavo lottando con lo stesso problema fino a quando non sono arrivato a questo. Spero che qualcuno lo trovi utile.

    public static String removeZero(double number) {
        DecimalFormat format = new DecimalFormat("#.###########");
        return format.format(number);
    }

5
new DecimalFormat("00.#").format(20.236)
//out =20.2

new DecimalFormat("00.#").format(2.236)
//out =02.2
  1. 0 per il numero minimo di cifre
  2. Rende # cifre

sebbene ciò possa fornire una soluzione alla domanda, è buona norma aggiungere una breve spiegazione affinché la comunità possa beneficiare (e apprendere) dalla risposta
blurfus,

4
String s = String.valueof("your int variable");
while (g.endsWith("0") && g.contains(".")) {
    g = g.substring(0, g.length() - 1);
    if (g.endsWith("."))
    {
        g = g.substring(0, g.length() - 1);
    }
}

dovresti invece cercare la prima cifra diversa da zero da destra e quindi utilizzare la sottostringa (e verificare anche che la stringa contenga "." ovviamente). in questo modo, non riuscirai a creare così tante stringhe temporanee lungo la strada.
sviluppatore Android

3

Risposta in ritardo ma ...

Hai detto che hai scelto di memorizzare i tuoi numeri con il doppio tipo . Penso che questo potrebbe essere la radice del problema perché ti costringe a memorizzare numeri interi in doppio (e quindi perdere le informazioni iniziali sulla natura del valore). Che dire di memorizzare i tuoi numeri nelle istanze della classe Number (superclasse di Double e Integer) e fare affidamento sul polimorfismo per determinare il formato corretto di ciascun numero?

So che potrebbe non essere accettabile il refactoring di una parte intera del codice a causa di ciò, ma potrebbe produrre l'output desiderato senza ulteriore codice / casting / analisi.

Esempio:

import java.util.ArrayList;
import java.util.List;

public class UseMixedNumbers {

    public static void main(String[] args) {
        List<Number> listNumbers = new ArrayList<Number>();

        listNumbers.add(232);
        listNumbers.add(0.18);
        listNumbers.add(1237875192);
        listNumbers.add(4.58);
        listNumbers.add(0);
        listNumbers.add(1.2345);

        for (Number number : listNumbers) {
            System.out.println(number);
        }
    }

}

Produrrà il seguente output:

232
0.18
1237875192
4.58
0
1.2345

javascript ha fatto la stessa scelta a proposito :)
Pirolistico

@Pyrolistical Puoi spiegare un po 'di più la tua affermazione? Non è del tutto chiaro per me ... :)
Scoperto il

2

Questo è quello che mi è venuto in mente:

  private static String format(final double dbl) {
    return dbl % 1 != 0 ? String.valueOf(dbl) : String.valueOf((int) dbl);
  }

Un solo liner, lancia solo int se è davvero necessario


1
Ripetendo ciò che Felix Edelmann ha detto altrove: ciò creerà una stringa indipendente dalla locale, che potrebbe non essere sempre appropriata per l'utente.
JJ Brown

punto giusto, per il mio caso d'uso questo non è stato un problema, non sono del tutto sicuro in questo momento ma penso che si potrebbe usare String.format (con il locale desiderato) invece di valueOf
keisar

2

Formatta il prezzo con raggruppamento, arrotondamento, senza zeri non necessari (in doppio).

Regole:

  1. Nessuno zero alla fine ( 2.0000 = 2; 1.0100000 = 1.01)
  2. Due cifre al massimo dopo un punto ( 2.010 = 2.01; 0.20 = 0.2)
  3. Arrotondamento dopo la seconda cifra dopo un punto ( 1.994 = 1.99; 1.995 = 2; 1.006 = 1.01; 0.0006 -> 0)
  4. Resi 0( null/-0 = 0)
  5. Aggiunge $( = $56/-$56)
  6. Raggruppamento ( 101101.02 = $101,101.02)

Altri esempi:

-99.985 = -$99.99

10 = $10

10.00 = $10

20.01000089 = $20.01

Scritto in Kotlin come divertente estensione di Double (causa utilizzata in Android), ma può essere facilmente convertito in Java, poiché sono state utilizzate le classi Java.

/**
 * 23.0 -> $23
 *
 * 23.1 -> $23.1
 *
 * 23.01 -> $23.01
 *
 * 23.99 -> $23.99
 *
 * 23.999 -> $24
 *
 * -0.0 -> $0
 *
 * -5.00 -> -$5
 *
 * -5.019 -> -$5.02
 */
fun Double?.formatUserAsSum(): String {
    return when {
        this == null || this == 0.0 -> "$0"
        this % 1 == 0.0 -> DecimalFormat("$#,##0;-$#,##0").format(this)
        else -> DecimalFormat("$#,##0.##;-$#,##0.##").format(this)
    }
}

Come usare:

var yourDouble: Double? = -20.00
println(yourDouble.formatUserAsSum()) // will print -$20

yourDouble = null
println(yourDouble.formatUserAsSum()) // will print $0

Informazioni su DecimalFormat : https://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html


1
public static String fmt(double d) {
    String val = Double.toString(d);
    String[] valArray = val.split("\\.");
    long valLong = 0;
    if(valArray.length == 2){
        valLong = Long.parseLong(valArray[1]);
    }
    if (valLong == 0)
        return String.format("%d", (long) d);
    else
        return String.format("%s", d);
}

Ho dovuto usare questa causa d == (long)dmi stava dando violazione nel rapporto sonar


1
float price = 4.30;
DecimalFormat format = new DecimalFormat("0.##"); // Choose the number of decimal places to work with in case they are different than zero and zero value will be removed
format.setRoundingMode(RoundingMode.DOWN); // choose your Rounding Mode
System.out.println(format.format(price));

questo è il risultato di alcuni test:

4.30     => 4.3
4.39     => 4.39  // Choose format.setRoundingMode(RoundingMode.UP) to get 4.4
4.000000 => 4
4        => 4

Che dire di 1.23450000?
Alex78191

1.23450000 => 1.23
Ahmed Mihoub

0

Ecco due modi per raggiungerlo. Innanzitutto, il modo più breve (e probabilmente migliore):

public static String formatFloatToString(final float f)
  {
  final int i=(int)f;
  if(f==i)
    return Integer.toString(i);
  return Float.toString(f);
  }

Ed ecco il modo più lungo e probabilmente peggiore:

public static String formatFloatToString(final float f)
  {
  final String s=Float.toString(f);
  int dotPos=-1;
  for(int i=0;i<s.length();++i)
    if(s.charAt(i)=='.')
      {
      dotPos=i;
      break;
      }
  if(dotPos==-1)
    return s;
  int end=dotPos;
  for(int i=dotPos+1;i<s.length();++i)
    {
    final char c=s.charAt(i);
    if(c!='0')
      end=i+1;
    }
  final String result=s.substring(0,end);
  return result;
  }

1
a volte, quando rendi le cose più semplici, il codice dietro è più complesso e meno ottimizzato ... ma sì, puoi usare molte funzioni API integrate ...
sviluppatore Android

1
Dovresti iniziare con semplici e una volta determinato che hai un problema di prestazioni, allora e solo allora dovresti ottimizzare. Il codice è per l'umano leggere ancora e ancora. Farlo funzionare velocemente è secondario. Non usando l'API standard quando possibile, è più probabile che tu introduca dei bug e rende solo più difficile cambiare in futuro.
Pirolistico

3
Direi che il codice che scrivi così non sarà più veloce. La JVM è molto intelligente e in realtà non sai quanto è veloce o lento qualcosa fino a quando non lo profili. I problemi di prestazioni possono essere rilevati e risolti quando diventa un problema. Non dovresti ottimizzarlo prematuramente per questo. Scrivi il codice per la lettura delle persone, non per come immagini che la macchina lo eseguirà. Una volta che diventa un problema di prestazioni, riscrivi il codice con un profiler.
Pirolistico

2
Qualcun altro ha modificato la risposta per migliorare la formattazione del codice. Stavo rivedendo diverse dozzine di modifiche per l'approvazione e avrei approvato la loro modifica qui, ma le modifiche erano incoerenti, quindi le ho riparate. Ho anche migliorato la grammatica degli snippet di testo.
Steve Vinoski,

1
Non capisco. Se hai detto che la formattazione non aveva importanza, perché hai dedicato del tempo a cambiarla?
OrhanC1,

0

Per Kotlin puoi usare l'estensione come:

fun Double.toPrettyString() =
    if(this - this.toLong() == 0.0)
        String.format("%d", this.toLong())
    else
        String.format("%s",this)

0

Ecco un'altra risposta che ha un'opzione per aggiungere il decimale SOLO SE il decimale non era zero.

   /**
     * Example: (isDecimalRequired = true)
     * d = 12345
     * returns 12,345.00
     *
     * d = 12345.12345
     * returns 12,345.12
     *
     * ==================================================
     * Example: (isDecimalRequired = false)
     * d = 12345
     * returns 12,345 (notice that there's no decimal since it's zero)
     *
     * d = 12345.12345
     * returns 12,345.12
     *
     * @param d float to format
     * @param zeroCount number decimal places
     * @param isDecimalRequired true if it will put decimal even zero,
     * false will remove the last decimal(s) if zero.
     */
    fun formatDecimal(d: Float? = 0f, zeroCount: Int, isDecimalRequired: Boolean = true): String {
        val zeros = StringBuilder()

        for (i in 0 until zeroCount) {
            zeros.append("0")
        }

        var pattern = "#,##0"

        if (zeros.isNotEmpty()) {
            pattern += ".$zeros"
        }

        val numberFormat = DecimalFormat(pattern)

        var formattedNumber = if (d != null) numberFormat.format(d) else "0"

        if (!isDecimalRequired) {
            for (i in formattedNumber.length downTo formattedNumber.length - zeroCount) {
                val number = formattedNumber[i - 1]

                if (number == '0' || number == '.') {
                    formattedNumber = formattedNumber.substring(0, formattedNumber.length - 1)
                } else {
                    break
                }
            }
        }

        return formattedNumber
    }

-1

Ecco una risposta che funziona davvero (combinazione di risposte diverse qui)

public static String removeTrailingZeros(double f)
{
    if(f == (int)f) {
        return String.format("%d", (int)f);
    }
    return String.format("%f", f).replaceAll("0*$", "");
}

1
non hai sostituito il PUNTO, ad esempio, "100.0" verrà convertito in "100".
VinceStyling,

se (f == (int) f) se ne occupa.
Martin Klosi,

2
Errore f = 9999999999,00
Dawood ibn Kareem,

-4

So che questo è un thread molto vecchio .. Ma penso che il modo migliore per farlo sia il seguente:

public class Test {

    public static void main(String args[]){
        System.out.println(String.format("%s something",new Double(3.456)));
        System.out.println(String.format("%s something",new Double(3.456234523452)));
        System.out.println(String.format("%s something",new Double(3.45)));
        System.out.println(String.format("%s something",new Double(3)));
    }
}

Produzione:

3.456 something
3.456234523452 something
3.45 something
3.0 something

L'unico problema è l'ultimo in cui .0 non viene rimosso. Ma se sei in grado di conviverci, allora funziona meglio. % .2f lo arrotonderà alle ultime 2 cifre decimali. Così sarà DecimalFormat. Se hai bisogno di tutte le cifre decimali ma non degli zeri finali, allora funziona meglio.


2
DecimalFormat con formato "#. ##" non manterrà 0 extra se non sono necessari: System.out.println(new java.text.DecimalFormat("#.##").format(1.0005));stamperà1
Aleks G

questo è il mio punto. Cosa succede se si desidera visualizzare 0.0005 se ce ne sono. Lo arrotonderai di 2 cifre decimali.
Sethu,

L'OP chiede come stampare i valori interi memorizzati in doppio :)
Aleks G

-8
String s = "1.210000";
while (s.endsWith("0")){
    s = (s.substring(0, s.length() - 1));
}

Questo farà sì che la stringa lasci cadere gli 0-s di coda.


1
Questa è una buona soluzione alla domanda, se fossero interessati a eliminare solo gli zero finali, come cambieresti il ​​tuo codice per tagliare anche un punto decimale finale? cioè "1."
Bakoyaro,

29
Fai attenzione, la tua soluzione convertirà 1000 in 1, il che è sbagliato.
Aleks G,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.