Wie kann man schwebende Zahlen ohne unnötige Dezimalzahl 0 in String formatieren?

496

Ein 64-Bit-Double kann eine ganze Zahl +/- 2 53 genau darstellen

Angesichts dieser Tatsache verwende ich für alle meine Typen einen Doppeltyp als Einzeltyp, da meine größte Ganzzahl vorzeichenloses 32-Bit ist.

Aber jetzt muss ich diese Pseudo-Ganzzahlen drucken, aber das Problem ist, dass sie auch mit tatsächlichen Doppelwerten gemischt werden.

Wie drucke ich diese Doppelbilder gut in Java?

Ich habe versucht String.format("%f", value), was nah ist, außer dass ich viele nachgestellte Nullen für kleine Werte bekomme.

Hier ist eine Beispielausgabe von %f

232.00000000
0,18000000000
1237875192.0
4.5800000000
0,00000000
1.23450000

Was ich will ist:

232
0,18
1237875192
4.58
0
1,2345

Sicher, ich kann eine Funktion schreiben, um diese Nullen zu trimmen, aber das ist ein großer Leistungsverlust aufgrund von String-Manipulationen. Kann ich es mit einem anderen Formatcode besser machen?

BEARBEITEN

Die Antworten von Tom E. und Jeremy S. sind nicht akzeptabel, da beide willkürlich auf 2 Dezimalstellen gerundet werden. Bitte verstehen Sie das Problem, bevor Sie antworten.

BEARBEITEN 2

Bitte beachten Sie, dass String.format(format, args...)ist locale-abhängig (siehe Antworten unten).

Pyrolistisch
quelle
Wenn alles, was Sie wollen, ganze Zahlen sind, warum nicht eine lange verwenden? Sie erhalten mehr Knall bei 2 ^ 63-1, keine umständliche Formatierung und bessere Leistung.
Basszero
14
Weil einige Werte tatsächlich doppelt sind
Pyrolistical
2
Einige Fälle, in denen dieses Problem auftrat, waren ein Fehler, der in JDK 7 behoben wurde
Pyrolistical
Ist es nur ich oder ist JavaScript bei der Konvertierung von Zahlen in Zeichenfolgen 100% besser als Java?
Andy
System.out.println("YOUR STRING" + YOUR_DOUBLE_VARIABLE);
Shayan Amani

Antworten:

399

Wenn die Idee darin besteht, Ganzzahlen zu drucken, die als Doppelte gespeichert sind, als wären sie Ganzzahlen, und ansonsten die Doppelten mit der minimal erforderlichen Genauigkeit zu drucken:

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

Produziert:

232
0.18
1237875192
4.58
0
1.2345

Und ist nicht auf String-Manipulation angewiesen.

JasonD
quelle
9
Einverstanden, das ist eine schlechte Antwort, benutze sie nicht. Es funktioniert nicht mit einem Wert, der doublegrößer als der Maximalwert intist. Auch longdamit würde es bei großen Zahlen noch scheitern. Außerdem wird ein String in Exponentialform, z. B. "1.0E10", für große Werte zurückgegeben, was der Fragesteller wahrscheinlich nicht wünscht. Verwenden Sie %fanstelle der %sZeichenfolge im zweiten Format, um dies zu beheben.
jlh
26
Das OP erklärte ausdrücklich, dass die Ausgabe nicht mit formatiert werden soll %f. Die Antwort ist spezifisch für die beschriebene Situation und die gewünschte Ausgabe. Das OP schlug vor, dass ihr Maximalwert ein 32-Bit-Int ohne Vorzeichen war, was intakzeptabel war (vorzeichenlos, in Java nicht vorhanden, und kein Exemplar war problematisch), aber das Wechseln intzu longist eine triviale Lösung, wenn die Situation anders ist.
JasonD
1
Wo in der Frage steht, dass es das nicht tun sollte?
JasonD
6
String.format("%s",d)??? Sprechen Sie über unnötigen Overhead. Verwenden Sie Double.toString(d). Gleiches gilt für die anderen : Long.toString((long)d).
Andreas
15
Das Problem ist, dass %ses mit Locales nicht funktioniert. Auf Deutsch verwenden wir ein "," anstelle eines "." in Dezimalzahlen. Während String.format(Locale.GERMAN, "%f", 1.5)"1.500000" zurückgegeben wird, wird String.format(Locale.GERMAN, "%s", 1.5)"1.5" zurückgegeben - mit einem ".", Was in deutscher Sprache falsch ist. Gibt es auch eine vom Gebietsschema abhängige Version von "% s"?
Felix Edelmann
414
new DecimalFormat("#.##").format(1.199); //"1.2"

Wie in den Kommentaren erwähnt, ist dies nicht die richtige Antwort auf die ursprüngliche Frage.
Das heißt, es ist eine sehr nützliche Möglichkeit, Zahlen ohne unnötige nachgestellte Nullen zu formatieren.

Tom Esterez
quelle
16
Ein wichtiger Hinweis hierbei ist, dass 1.1 ohne nachgestellte Nullen ordnungsgemäß als "1.1" formatiert wird.
Steve Pomeroy
53
Und wenn Sie zufällig eine bestimmte Anzahl von nachgestellten Nullen möchten (z. B. wenn Sie Geldbeträge drucken), können Sie '0' anstelle von '#' verwenden (dh neues DecimalFormat ("0.00"). Format (Betrag);) ist nicht das, was OP wollte, kann aber als Referenz nützlich sein.
TJ Ellis
22
Ja, als ursprünglicher Autor der Frage ist dies die falsche Antwort. Komisch, wie viele Stimmen es gibt. Das Problem bei dieser Lösung ist, dass sie willkürlich auf 2 Dezimalstellen gerundet wird.
Pyrolistical
11
@ Mazyod, weil Sie immer einen Gleitkomma mit mehr Dezimalstellen als dem Format übergeben können. Das heißt, Code zu schreiben, der die meiste Zeit funktioniert, aber nicht alle Randfälle abdeckt.
Pyrolistical
15
@Pyrolistical - IMHO, es gibt viele positive Stimmen, denn obwohl dies die falsche Lösung für Sie ist, ist es die richtige Lösung für 99% + derjenigen, die diese Fragen und Antworten finden: Normalerweise sind die letzten paar Ziffern eines Doppels "Rauschen". Dadurch wird die Ausgabe unübersichtlich und die Lesbarkeit beeinträchtigt. Daher bestimmt der Programmierer, wie viele Ziffern für die Person, die die Ausgabe liest, von Vorteil sind, und gibt diese Anzahl an. Eine häufige Situation ist, dass sich kleine mathematische Fehler angesammelt haben, sodass ein Wert möglicherweise 12.000000034 ist, aber es vorziehen würde, auf 12 zu runden und kompakt als "12" anzuzeigen. Und "12.340000056" => "12.34".
ToolmakerSteve
226
String.format("%.2f", value) ;
Jeremy Slade
quelle
13
Das ist richtig, druckt aber immer nachgestellte Nullen, auch wenn es keinen Bruchteil gibt. String.format ("%. 2f, 1.0005) druckt 1,00 und nicht 1. Gibt es einen Formatbezeichner, um einen Bruchteil nicht zu drucken, wenn er nicht existiert?
Emre Yazici
87
Abgestimmt, da die Frage alle nachgestellten Nullen entfernen soll und diese Antwort immer zwei Gleitkommazahlen hinterlässt, unabhängig davon, ob sie Null sind.
Zulaxia
Das DecimalFormat war ein netter Trick - obwohl ich diesen letztendlich für meine Situation verwendet habe (Timer auf Spielebene), da die nachfolgenden Nullen besser aussahen.
Timothy Lee Russell
2
Ich denke, Sie können die nachfolgenden Nullen korrekt behandeln, indem Sie g anstelle von f verwenden.
Peter Ajtai
3
Ich habe diese Lösung in einem Produktionssystem mit "% .5f" verwendet, und es ist wirklich sehr, sehr schlecht, verwenden Sie sie nicht ... weil sie Folgendes gedruckt hat: 5.12E-4 statt 0,000512
hamish
87

Zusamenfassend:

Wenn Sie nachgestellte Nullen und Gebietsschemaprobleme beseitigen möchten, sollten Sie Folgendes verwenden:

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

Erläuterung:

Warum andere Antworten nicht zu mir passten:

  • Double.toString()oder System.out.printlnoder FloatingDecimal.toJavaFormatStringverwendet wissenschaftliche Notationen, wenn double kleiner als 10 ^ -3 oder größer oder gleich 10 ^ 7 ist

    double myValue = 0.00000021d;
    String.format("%s", myvalue); //output: 2.1E-7
  • Bei Verwendung %fbeträgt die Standard-Dezimalgenauigkeit 6, andernfalls können Sie sie fest codieren. Wenn Sie jedoch weniger Dezimalstellen haben, werden zusätzliche Nullen hinzugefügt. Beispiel:

    double myValue = 0.00000021d;
    String.format("%.12f", myvalue); //output: 0.000000210000
  • indem Sie eine Dezimalgenauigkeit verwenden setMaximumFractionDigits(0);oder %.0fentfernen, die für Ganzzahlen / Longs, aber nicht für Double in Ordnung ist

    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
  • Mit DecimalFormat sind Sie lokal abhängig. Im französischen Gebietsschema ist das Dezimaltrennzeichen ein Komma, kein Punkt:

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

    Durch die Verwendung des Gebietsschemas ENGLISH wird sichergestellt, dass Sie überall dort, wo Ihr Programm ausgeführt wird, einen Punkt für das Dezimaltrennzeichen erhalten

Warum dann 340 für setMaximumFractionDigits?

Zwei Gründe :

  • setMaximumFractionDigitsakzeptiert eine Ganzzahl, aber für die Implementierung sind maximal Ziffern zulässig, von DecimalFormat.DOUBLE_FRACTION_DIGITSdenen 340 entspricht
  • Double.MIN_VALUE = 4.9E-324 Mit 340 Stellen können Sie also Ihre doppelte und lockere Präzision nicht abrunden
JBE
quelle
Dies funktioniert nicht für ganze Zahlen, z. B. wird "2" zu "2".
Kap
Danke, ich habe die Antwort 0#.
korrigiert,
Sie verwenden nicht die Konstante, DecimalFormat.DOUBLE_FRACTION_DIGITSsondern den Wert 340, für den Sie einen Kommentar abgeben, um zu zeigen, dass er gleich ist DecimalFormat.DOUBLE_FRACTION_DIGITS. Warum nicht einfach die Konstante benutzen ???
Maarten Bodewes
1
Da dieses Attribut nicht öffentlich ist ... ist es "
paketfreundlich
4
Vielen Dank! Tatsächlich ist diese Antwort die einzige, die wirklich allen in der Frage genannten Anforderungen entspricht - sie zeigt keine unnötigen Nullen an, rundet die Zahlen nicht und ist vom Gebietsschema abhängig. Großartig!
Felix Edelmann
26

Warum nicht:

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

Dies sollte mit den von Double unterstützten Extremwerten funktionieren. Ausbeuten:

0.12
12
12.144252
0
Valeriu Paloş
quelle
2
Ich bevorzuge diese Antwort, mit der wir keine Typkonvertierung durchführen müssen.
Jeff T.
Kurze Erklärung: "%s"Grundsätzlich Anrufe, d.toString()aber es funktioniert nicht mit intoder wenn d==null!
Neph
24

Auf meinem Computer ist die folgende Funktion ungefähr siebenmal schneller als die von JasonDs Antwort bereitgestellte Funktion , da sie Folgendes vermeidet String.format:

public static String prettyPrint(double d) {
  int i = (int) d;
  return d == i ? String.valueOf(i) : String.valueOf(d);
}
Rok Strniša
quelle
1
Hmm, das berücksichtigt keine Gebietsschemas, aber JasonDs auch nicht.
TWiStErRob
22

Meine 2 Cent:

if(n % 1 == 0) {
    return String.format(Locale.US, "%.0f", n));
} else {
    return String.format(Locale.US, "%.1f", n));
}
Fernando Gallego
quelle
2
Oder einfach nur return String.format(Locale.US, (n % 1 == 0 ? "%.0f" : "%.1f"), n);.
MC Emperor
scheitern, wenn 23.00123 ==> 23.00
aswzen
11

Nein, egal.

Der Leistungsverlust aufgrund von String-Manipulationen ist Null.

Und hier ist der Code, um das Ende danach zu kürzen %f

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

    return number.replaceAll("\\.?0*$", "");
}
Pyrolistisch
quelle
7
Ich habe abgelehnt, weil Ihre Lösung nicht der beste Weg ist. Schauen Sie sich String.format an. Sie müssen den richtigen Formattyp verwenden, in diesem Fall float. Schau dir meine obige Antwort an.
jjnguy
6
Ich habe abgestimmt, weil ich das gleiche Problem habe und niemand hier das Problem zu verstehen scheint.
Obay
1
Downvoted als das in Toms Beitrag erwähnte DecimalFormat ist genau das, wonach Sie gesucht haben. Es entfernt Nullen ziemlich effektiv.
Steve Pomeroy
4
Vielleicht möchte er die Nullen OHNE Rundung kürzen? PS @Pyrolistical, sicherlich können Sie einfach number.replaceAll (".? 0 * $", "") verwenden; (nachdem enthält (".") natürlich)
Rehno Lindeque
1
Ok, wie könnten Sie dann mein Ziel mit dem DecimalFormat erreichen?
Pyrolistical
8

Verwenden Sie ein DecimalFormatundsetMinimumFractionDigits(0)

vlazzle
quelle
Ich würde hinzufügen setMaximumFractionDigits(2)und setGroupingUsed(false)(OPs erwähnen es nicht, aber aus dem Beispiel scheint es, dass es erforderlich ist). Auch ein kleiner Testfall tut nicht weh, da er in diesem Fall trivial ist. Trotzdem, da ich denke, dass es die einfachste Lösung ist, ist ein Upvote ein Upvote :)
Acrespo
6
if (d == Math.floor(d)) {
    return String.format("%.0f", d);
} else {
    return Double.toString(d);
}
fop6316
quelle
1
Ich denke, ich werde Ihnen folgen: D
Aeracode
5

Bitte beachten Sie, dass String.format(format, args...)ist locale-abhängig , da es formatiert das Standardgebietsschema des Benutzers verwenden, die wahrscheinlich mit Komma und sogar Räumen im Innern wie ist 123 456789 oder 123,456.789 , die nicht genau sein kann , was Sie erwarten.

Sie können es vorziehen, zu verwenden String.format((Locale)null, format, args...) .

Zum Beispiel,

    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));

druckt

123456,789000
123456,789000
123456.789000

und das ist was wird String.format(format, args...) in verschiedenen Ländern geschehen.

EDIT Ok, da es eine Diskussion über Formalitäten gegeben hat:

    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;
}
18446744073709551615
quelle
1
Sie sollten dies als Kommentar zur akzeptierten Antwort
hinzufügen
Kommentare erlauben nicht die Länge oder das Format dieses Nachtrags. Da es möglicherweise nützliche Informationen hinzufügt, denke ich, dass es so wie es ist erlaubt sein sollte, anstatt gelöscht zu werden.
Terry Jan Reedy
5

Ich habe eine DoubleFormattergroße Anzahl von Doppelwerten effizient in einen schönen / präsentablen String konvertiert:

double horribleNumber = 3598945.141658554548844; 
DoubleFormatter df = new DoubleFormatter(4,6); //4 = MaxInteger, 6 = MaxDecimal
String beautyDisplay = df.format(horribleNumber);
  • Wenn der ganzzahlige Teil von V mehr als MaxInteger hat => V im Wissenschaftlerformat (1.2345e + 30) anzeigen, andernfalls im Normalformat 124.45678.
  • Die MaxDecimal entscheiden über die Anzahl der Dezimalstellen (Trimmen mit Banker-Rundung).

Hier der Code:

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();
    }
}

Hinweis: Ich habe 2 Funktionen aus der GUAVA-Bibliothek verwendet. Wenn Sie GUAVA nicht verwenden, codieren Sie es selbst:

/**
 * 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();
}
Hiep
quelle
1
Wenn Sie die Genauigkeit kennen, verwenden Sie ein BigDecimal. Siehe docs.oracle.com/javase/1.5.0/docs/api/java/math/…
Pyrolistical
5

Dieser wird die Arbeit gut erledigen, ich weiß, dass das Thema alt ist, aber ich hatte mit dem gleichen Problem zu kämpfen, bis ich dazu kam. Ich hoffe, jemand findet es nützlich.

    public static String removeZero(double number) {
        DecimalFormat format = new DecimalFormat("#.###########");
        return format.format(number);
    }
Bialy
quelle
5
new DecimalFormat("00.#").format(20.236)
//out =20.2

new DecimalFormat("00.#").format(2.236)
//out =02.2
  1. 0 für die minimale Anzahl von Ziffern
  2. Rendert # Ziffern
Hossein Hajizadeh
quelle
Dies kann zwar eine Lösung für die Frage darstellen, es ist jedoch
empfehlenswert
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);
    }
}
Kamal
quelle
Sie sollten stattdessen einfach nach der ersten Ziffer ungleich Null von rechts suchen und dann den SubString verwenden (und natürlich auch überprüfen, ob die Zeichenfolge "." enthält). Auf diese Weise werden Sie unterwegs nicht so viele temporäre Zeichenfolgen erstellen.
Android-Entwickler
3

Späte Antwort aber ...

Sie sagten , Sie wählen Sie Ihre Zahlen mit dem speichern Doppel Typ . Ich denke, dies könnte die Wurzel des Problems sein, da Sie dadurch gezwungen werden, Ganzzahlen in Doppelwerten zu speichern (und daher die anfänglichen Informationen über die Art des Werts zu verlieren). Wie wäre es, wenn Sie Ihre Zahlen in Instanzen der Number- Klasse (Superklasse von Double und Integer) speichern und sich auf Polymorphismus verlassen, um das richtige Format für jede Zahl zu bestimmen?

Ich weiß, dass es aus diesem Grund möglicherweise nicht akzeptabel ist, einen ganzen Teil Ihres Codes umzugestalten, aber es könnte die gewünschte Ausgabe ohne zusätzlichen Code / Casting / Parsing erzeugen.

Beispiel:

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);
        }
    }

}

Erzeugt die folgende Ausgabe:

232
0.18
1237875192
4.58
0
1.2345
Gepunktet
quelle
Javascript traf übrigens die gleiche Wahl :)
Pyrolistical
@Pyrolistical Kannst du deine Aussage etwas näher erläutern? Es ist nicht ganz klar für mich ... :)
Spotted
2

Das habe ich mir ausgedacht:

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

Einfacher Einzeiler, wird nur bei Bedarf in int umgewandelt

Keisar
quelle
1
Wiederholen Sie, was Felix Edelmann an anderer Stelle gesagt hat: Dadurch wird eine vom Gebietsschema unabhängige Zeichenfolge erstellt, die für den Benutzer möglicherweise nicht immer geeignet ist.
JJ Brown
Fair Point, für meinen Anwendungsfall war dies kein Problem, ich bin mir momentan nicht ganz sicher, aber ich denke, man könnte String.format (mit dem gewünschten Gebietsschema) anstelle von valueOf
keisar
2

Formatieren Sie den Preis mit Gruppierung, Rundung, ohne unnötige Nullen (doppelt).

Regeln:

  1. Keine Nullen am Ende ( 2.0000 = 2; 1.0100000 = 1.01)
  2. Maximal zwei Stellen nach einem Punkt ( 2.010 = 2.01; 0.20 = 0.2)
  3. Rundung nach der 2. Ziffer nach einem Punkt ( 1.994 = 1.99; 1.995 = 2; 1.006 = 1.01; 0.0006 -> 0)
  4. Rückgabe 0( null/-0 = 0)
  5. Fügt hinzu $( = $56/-$56)
  6. Gruppierung ( 101101.02 = $101,101.02)

Mehr Beispiele:

-99.985 = -$99.99

10 = $10

10.00 = $10

20.01000089 = $20.01

Geschrieben in Kotlin als lustige Erweiterung von Double (Ursache in Android), kann aber leicht in Java konvertiert werden, da Java-Klassen verwendet wurden.

/**
 * 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)
    }
}

Wie benutzt man:

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

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

Informationen zu DecimalFormat : https://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html

oxied
quelle
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);
}

Ich musste diese Ursache nutzen d == (long)d, um mich im Sonarbericht zu verletzen

ShAkKiR
quelle
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));

Dies ist das Ergebnis einiger Tests:

4.30     => 4.3
4.39     => 4.39  // Choose format.setRoundingMode(RoundingMode.UP) to get 4.4
4.000000 => 4
4        => 4
Ahmed Mihoub
quelle
Was ist mit 1.23450000?
Alex78191
1.23450000 => 1.23
Ahmed Mihoub
0

Hier sind zwei Möglichkeiten, um dies zu erreichen. Erstens der kürzere (und wahrscheinlich bessere) Weg:

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

Und hier ist der längere und wahrscheinlich schlechtere Weg:

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;
  }
Android-Entwickler
quelle
1
Manchmal, wenn Sie die Dinge einfacher machen, ist der Code dahinter komplexer und weniger optimiert ... aber ja, Sie können viele integrierte API-Funktionen verwenden ...
Android-Entwickler
1
Sie sollten mit einfach beginnen und sobald Sie festgestellt haben, dass Sie ein Leistungsproblem haben, sollten Sie nur dann optimieren. Code ist für den Menschen immer wieder zu lesen. Es ist zweitrangig, es schnell laufen zu lassen. Wenn Sie die Standard-API nach Möglichkeit nicht verwenden, treten mit größerer Wahrscheinlichkeit Fehler auf, und es wird nur schwieriger, Änderungen in Zukunft vorzunehmen.
Pyrolistical
3
Ich würde argumentieren, dass Code, den Sie so schreiben, NICHT schneller sein wird. Die JVM ist sehr intelligent und Sie wissen erst, wie schnell oder langsam etwas ist, wenn Sie es profilieren. Leistungsprobleme können erkannt und behoben werden, wenn sie zu einem Problem werden. Sie sollten nicht vorzeitig dafür optimieren. Schreiben Sie Code, damit die Leute ihn lesen können, und nicht, wie Sie sich vorstellen, dass der Computer ihn ausführen wird. Sobald es zu einem Leistungsproblem wird, schreiben Sie den Code mit einem Profiler neu.
Pyrolistical
2
Jemand anderes hat die Antwort bearbeitet, um die Code-Formatierung zu verbessern. Ich habe mehrere Dutzend Änderungen auf Genehmigung überprüft und wollte ihre Bearbeitung hier genehmigen, aber die Änderungen waren inkonsistent, sodass ich sie behoben habe. Ich habe auch die Grammatik der Textausschnitte verbessert.
Steve Vinoski
1
Ich verstehe nicht Wenn Sie sagten, dass die Formatierung keine Rolle spielt, warum haben Sie sich die Zeit genommen, sie wieder zu ändern?
OrhanC1
0

Für Kotlin können Sie folgende Erweiterungen verwenden:

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

Hier ist eine weitere Antwort, bei der NUR eine Dezimalstelle angehängt werden kann, wenn die Dezimalstelle nicht Null war.

   /**
     * 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
    }
Tenten Ponce
quelle
-1

Hier ist eine Antwort, die tatsächlich funktioniert (Kombination verschiedener Antworten hier)

public static String removeTrailingZeros(double f)
{
    if(f == (int)f) {
        return String.format("%d", (int)f);
    }
    return String.format("%f", f).replaceAll("0*$", "");
}
Martin Klosi
quelle
1
Sie haben den PUNKT nicht ersetzt. Beispiel: "100.0" wird in "100" konvertiert.
VinceStyling
if (f == (int) f) kümmert sich darum.
Martin Klosi
2
Fehlgeschlagen auf f = 9999999999.00
Dawood ibn Kareem
-4

Ich weiß, dass dies ein wirklich alter Thread ist. Aber ich denke, der beste Weg, dies zu tun, ist wie folgt:

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)));
    }
}

Ausgabe:

3.456 something
3.456234523452 something
3.45 something
3.0 something

Das einzige Problem ist das letzte, bei dem .0 nicht entfernt wird. Aber wenn Sie damit leben können, funktioniert dies am besten. % .2f rundet es auf die letzten 2 Dezimalstellen. DecimalFormat auch. Wenn Sie alle Dezimalstellen benötigen, aber nicht die nachgestellten Nullen, funktioniert dies am besten.

Sethu
quelle
2
DecimalFormat mit dem Format "#. ##" behält keine zusätzliche 0 bei, wenn sie nicht benötigt werden: System.out.println(new java.text.DecimalFormat("#.##").format(1.0005));wird gedruckt1
Aleks G
Das ist mein Punkt. Was ist, wenn die 0,0005 angezeigt werden soll, falls vorhanden? Sie runden es mit 2 Dezimalstellen.
Sethu
Das OP fragt, wie Ganzzahlwerte gedruckt werden sollen, die im Doppel gespeichert sind :)
Aleks G
-8
String s = "1.210000";
while (s.endsWith("0")){
    s = (s.substring(0, s.length() - 1));
}

Dadurch lässt der String die Tailing-0-s fallen.

Kadi
quelle
1
Dies ist eine gute Lösung für die Frage: Wenn Sie nur daran interessiert wären, nachgestellte Nullen zu löschen, wie würden Sie Ihren Code ändern, um auch einen nachgestellten Dezimalpunkt zu kürzen? dh "1."
Bakoyaro
29
Seien Sie vorsichtig, Ihre Lösung konvertiert 1000 in 1, was falsch ist.
Aleks G