Jak ładnie sformatować liczby zmiennoprzecinkowe na ciąg bez niepotrzebnego dziesiętnego 0?


496

64-bitowe podwójne może dokładnie reprezentować liczbę całkowitą +/- 2 53

Biorąc pod uwagę ten fakt, zdecydowałem się użyć podwójnego typu jako jednego typu dla wszystkich moich typów, ponieważ moja największa liczba całkowita jest bez znaku 32-bitowa.

Ale teraz muszę wydrukować te pseudo liczby całkowite, ale problem polega na tym, że są one również pomieszane z rzeczywistymi liczbami podwójnymi.

Jak więc ładnie wydrukować te kopie w Javie?

Próbowałem String.format("%f", value), co jest bliskie, ale dostaję dużo zer końcowych dla małych wartości.

Oto przykładowy wynik działania %f

232,00000000
0,18000000000
1237875192.0
4.5800000000
0,00000000
1,23450000

Chcę to:

232
0,18
1237875192
4.58
0
1,2345

Pewnie, że mogę napisać funkcję do przycinania tych zer, ale to duża utrata wydajności z powodu manipulacji ciągiem. Czy mogę zrobić lepiej z innym kodem formatu?

EDYTOWAĆ

Odpowiedzi Toma E. i Jeremy'ego S. są niedopuszczalne, ponieważ obaj arbitralnie zaokrąglają do 2 miejsc po przecinku. Proszę zrozumieć problem przed odpowiedzią.

EDYCJA 2

Pamiętaj, że zależyString.format(format, args...) to od ustawień regionalnych (patrz odpowiedzi poniżej).


Jeśli wszystko, czego chcesz, to liczby całkowite, dlaczego nie użyć długiej? Dostajesz więcej w 2 ^ 63-1, bez niewygodnego formatowania i lepszej wydajności.
basszero

14
Ponieważ niektóre wartości są w rzeczywistości
podwajane

2
Niektóre przypadki, w których wystąpił ten problem, to błąd naprawiony w JDK 7: stackoverflow.com/questions/7564525/…
Pyrolistic

Czy to tylko ja, czy JavaScript jest o 100% lepszy w konwersji liczb na ciąg znaków niż Java?
Andy

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

Odpowiedzi:


399

Jeśli chodzi o drukowanie liczb całkowitych przechowywanych jako liczby podwójne, tak jakby były liczbami całkowitymi, a w przeciwnym razie drukowanie liczb podwójnych z minimalną konieczną precyzją:

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

Produkuje:

232
0.18
1237875192
4.58
0
1.2345

I nie polega na manipulacji ciągiem.


9
Zgadzam się, to zła odpowiedź, nie używaj jej. Nie działa z wartością doublewiększą niż maksymalna int. Mimo longto nadal nie udałoby się to w przypadku ogromnych liczb. Ponadto zwróci ciąg w postaci wykładniczej, np. „1.0E10”, dla dużych wartości, co prawdopodobnie nie jest tym, czego chce pytający. Użyj %fzamiast %sw drugim formacie ciągu, aby to naprawić.
jlh

26
PO stwierdził wyraźnie, że nie chce formatowania danych wyjściowych za pomocą %f. Odpowiedź jest specyficzna dla opisanej sytuacji i pożądanego wyniku. OP zasugerował, że ich maksymalna wartość to 32-bitowy znak bez znaku int, który przyjąłem, że intjest akceptowalny (niepodpisany, że tak naprawdę nie istnieje w Javie i żaden przykład nie był problematyczny), ale zmiana intna longtrywialną poprawkę, jeśli sytuacja jest inna.
JasonD

1
Gdzie w pytaniu jest napisane, że nie powinno tego robić?
JasonD

6
String.format("%s",d)??? Mów o niepotrzebnych kosztach ogólnych. Zastosowanie Double.toString(d). Samo dla drugiej: Long.toString((long)d).
Andreas

15
Problem polega na tym, %sże nie działa z ustawieniami regionalnymi. W języku niemieckim używamy „,” zamiast „.” w liczbach dziesiętnych. Podczas gdy String.format(Locale.GERMAN, "%f", 1.5)zwraca „1 500 000”, String.format(Locale.GERMAN, "%s", 1.5)zwraca „1,5” - z „.”, Co w języku niemieckim jest fałszem. Czy istnieje także wersja „% s” zależna od ustawień regionalnych?
Felix Edelmann

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

Jak wskazano w komentarzach, nie jest to odpowiednia odpowiedź na pierwotne pytanie.
To powiedziawszy, jest to bardzo przydatny sposób formatowania liczb bez niepotrzebnych zer końcowych.


16
Ważną uwagą jest to, że 1.1 byłoby poprawnie sformatowane jako „1.1” bez żadnych zer końcowych.
Steve Pomeroy

53
A jeśli chcesz określonej liczby zer zerowych (np. Jeśli drukujesz kwoty pieniężne), możesz użyć „0” zamiast „#” (tj. Nowy DecimalFormat („0,00”). Format (kwota);) to nie jest tym, czego chciał OP, ale może być przydatny w celach informacyjnych.
TJ Ellis,

22
Tak, jako pierwotny autor pytania jest to NIEPRAWIDŁOWA odpowiedź. Śmieszne, ile jest głosów. Problem z tym rozwiązaniem polega na tym, że arbitralnie zaokrągla się do 2 miejsc po przecinku.
Pirolistyczny

11
@Mazyod, ponieważ zawsze możesz podać zmiennoprzecinkowy z większą liczbą dziesiętną niż format. To jest pisanie kodu, który będzie działał przez większość czasu, ale nie obejmie wszystkich przypadków brzegowych.
Pirolistyczny

15
@Pirolistyczny - IMHO, istnieje wiele pozytywnych opinii, ponieważ chociaż jest to niewłaściwe rozwiązanie dla ciebie, jest to właściwe rozwiązanie dla ponad 99% osób, które znajdują takie pytania i odpowiedzi: zwykle kilka ostatnich cyfr podwójnego to „szum”, które zaśmiecają wyjście, zakłócając czytelność. Dlatego programista określa, ile cyfr jest korzystnych dla osoby czytającej dane wyjściowe, i określa ich liczbę. Często zdarza się, że nagromadziły się małe błędy matematyczne, więc wartość może wynosić 12,000000034, ale wolałaby zaokrąglać do 12 i wyświetlać zwięźle jako „12”. I „12.340000056” => „12,34”.
ToolmakerSteve

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

13
To prawda, ale zawsze drukuje końcowe zera, nawet jeśli nie ma części ułamkowej. String.format ("%. 2f, 1.0005) drukuje 1,00, a nie 1. Czy jest jakiś specyfikator formatu, aby nie drukować części ułamkowej, jeśli nie istnieje?
Emre Yazici

87
Głosowano w dół, ponieważ pytanie wymaga usunięcia wszystkich zer końcowych, a ta odpowiedź zawsze pozostawi dwa zmiennoprzecinkowe, niezależnie od tego, czy wynosi zero.
Zulaxia

DecimalFormat był fajną sztuczką - chociaż ostatecznie użyłem tego w mojej sytuacji (licznik poziomów gry), ponieważ końcowe zera wyglądały lepiej.
Timothy Lee Russell,

2
Myślę, że możesz poprawnie obsługiwać końcowe zera, używając g zamiast f.
Peter Ajtai

3
Użyłem tego rozwiązania w systemie produkcyjnym z „% .5f”, i to jest naprawdę bardzo złe, nie używaj go ... ponieważ wydrukowało to: 5.12E-4 zamiast 0.000512
hamish

87

W skrócie:

Jeśli chcesz pozbyć się zer i problemów z ustawieniami regionalnymi, powinieneś użyć:

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

Wyjaśnienie:

Dlaczego inne odpowiedzi mi nie pasowały:

  • Double.toString()lub System.out.printlnlub FloatingDecimal.toJavaFormatStringwykorzystuje notacji naukowej, gdy podwójne jest mniejsza niż 10 ^ -3 lub większą niż lub równą 10 ^ 7

    double myValue = 0.00000021d;
    String.format("%s", myvalue); //output: 2.1E-7
  • przy użyciu %fdomyślna dokładność dziesiętna wynosi 6, w przeciwnym razie możesz ją zakodować na stałe, ale powoduje to dodanie dodatkowych zer, jeśli masz mniej miejsc po przecinku. Przykład:

    double myValue = 0.00000021d;
    String.format("%.12f", myvalue); //output: 0.000000210000
  • za pomocą setMaximumFractionDigits(0);lub %.0fusuwasz dokładność dziesiętną, co jest dobre dla liczb całkowitych / długich, ale nie dla podwójnej

    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
  • Korzystając z DecimalFormat, jesteś zależny lokalnie. W języku francuskim separatorem dziesiętnym jest przecinek, a nie kropka:

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

    Korzystanie z ustawień regionalnych w języku ANGIELSKIM zapewnia, że ​​otrzymasz punkt separatora dziesiętnego, niezależnie od miejsca działania programu

Po co więc używać 340 setMaximumFractionDigits?

Dwa powody :

  • setMaximumFractionDigitsakceptuje liczbę całkowitą, ale jej implementacja ma maksymalną dozwoloną liczbę, z DecimalFormat.DOUBLE_FRACTION_DIGITSczego 340
  • Double.MIN_VALUE = 4.9E-324 więc z 340 cyframi na pewno nie zaokrąglisz swojej podwójnej i luźnej precyzji

Nie działa to dla liczb całkowitych, np. „2” zmienia się na „2.”
kap

Dzięki, naprawiłem odpowiedź, używając wzorca 0zamiast#.
JBE

Nie używasz stałej, DecimalFormat.DOUBLE_FRACTION_DIGITSale używasz wartości 340, którą następnie dodajesz komentarz, aby pokazać, że jest ona równa DecimalFormat.DOUBLE_FRACTION_DIGITS. Dlaczego po prostu nie użyć stałej ???
Maarten Bodewes

1
Ponieważ ten atrybut nie jest publiczny ... jest „przyjazny dla pakietu”
JBE

4
Dzięki! W rzeczywistości ta odpowiedź jest jedyną, która naprawdę spełnia wszystkie wymagania wymienione w pytaniu - nie pokazuje zbędnych zer, nie zaokrągla liczb i jest zależna od ustawień regionalnych. Świetny!
Felix Edelmann

26

Dlaczego nie:

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

Powinno to działać z ekstremalnymi wartościami obsługiwanymi przez Double. Wydajność:

0.12
12
12.144252
0

2
Wolę tę odpowiedź, za pomocą której nie musimy wykonywać konwersji typów.
Jeff T.

Krótkie wyjaśnienie: w "%s"zasadzie dzwoni, d.toString()ale nie działa z intlub jeśli d==null!
Neph

24

Na moim komputerze następująca funkcja jest około 7 razy szybsza niż funkcja dostarczona przez odpowiedź JasonD , ponieważ unika String.format:

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

1
Hmm, to nie bierze pod uwagę ustawień regionalnych, ale też nie JasonD.
TWiStErRob

22

Moje 2 centy:

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

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

nie powiedzie się, gdy 23.00123 ==> 23.00
aswzen

11

Naw, nieważne.

Utrata wydajności w wyniku manipulacji ciągiem wynosi zero.

A oto kod do przycięcia końca po %f

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

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

7
Odrzuciłem głos, ponieważ twoje rozwiązanie nie jest najlepszym rozwiązaniem. Spójrz na String.format. Musisz użyć poprawnego typu formatu, zmiennoprzecinkowego w tym przypadku. Spójrz na moją powyższą odpowiedź.
jjnguy

6
Głosowałem, ponieważ mam ten sam problem i wydaje się, że nikt tutaj go nie rozumie.
Obay,

1
Przegłosowany jako DecimalFormat wspomniany w poście Toma jest dokładnie tym, czego szukałeś. Dość skutecznie zeruje zera.
Steve Pomeroy

4
W związku z tym może chce przyciąć zera BEZ zaokrąglania? PS @Pirolistyczny, na pewno możesz po prostu użyć number.replaceAll (".? 0 * $", ""); (po zawiera oczywiście („.”))
Rehno Lindeque

1
Ok, więc w jaki sposób byłbyś w stanie osiągnąć mój cel z DecimalFormat?
Pirolistyczny

8

Użyj a DecimalFormatisetMinimumFractionDigits(0)


Dodałbym setMaximumFractionDigits(2)i setGroupingUsed(false)(OP nie wspomina o tym, ale z przykładu wydaje się, że jest to wymagane). Ponadto mały przypadek testowy nie boli, ponieważ w tym przypadku jest trywialny. Mimo to, ponieważ uważam, że jest to najprostsze rozwiązanie, głosowanie jest głosowaniem pozytywnym :)
zgodnie z

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

1
Myślę, że
podążę

5

Pamiętaj, że String.format(format, args...)jest to zależne od ustawień regionalnych, ponieważ formatuje przy użyciu domyślnych ustawień narodowych użytkownika, to znaczy prawdopodobnie z przecinkami, a nawet spacjami wewnątrz, takimi jak 123 456,789 lub 123 456,789 , co może nie być dokładnie tym, czego oczekujesz.

Wolisz używać String.format((Locale)null, format, args...).

Na przykład,

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

odciski

123456,789000
123456,789000
123456.789000

i tak będzie String.format(format, args...)w różnych krajach.

EDYCJA Ok, ponieważ trwa dyskusja na temat formalności:

    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
powinieneś dodać to jako komentarz do zaakceptowanej odpowiedzi
pirolistyczny

Komentarze nie pozwalają na długość ani format tego dodatku. Ponieważ dodaje potencjalnie użytecznych informacji, myślę, że powinno być dozwolone, jak jest, a nie usunięte.
Terry Jan Reedy

5

Zrobiłem, DoubleFormatteraby efektywnie przekonwertować dużą liczbę podwójnych wartości na ładny / prezentowalny ciąg:

double horribleNumber = 3598945.141658554548844; 
DoubleFormatter df = new DoubleFormatter(4,6); //4 = MaxInteger, 6 = MaxDecimal
String beautyDisplay = df.format(horribleNumber);
  • Jeśli część całkowita V ma więcej niż MaxInteger => wyświetl V w formacie naukowym (1.2345e + 30), w przeciwnym razie wyświetl w normalnym formacie 124.45678.
  • MaxDecimal decyduje o liczbie cyfr dziesiętnych (przycinanie z zaokrągleniem bankierskim)

Oto kod:

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

Uwaga: Użyłem 2 funkcji z biblioteki GUAVA. Jeśli nie korzystasz z GUAVA, koduj sam:

/**
 * 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
Jeśli znasz dokładność, użyj BigDecimal. Zobacz docs.oracle.com/javase/1.5.0/docs/api/java/math/…
Pirolistyczny

5

Ten sprawi, że praca będzie dobrze wykonana, wiem, że temat jest stary, ale walczyłem z tym samym problemem, aż do tego doszedłem. Mam nadzieję, że ktoś uzna to za przydatne.

    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 dla minimalnej liczby cyfr
  2. Renderuje # cyfry

chociaż może to stanowić odpowiedź na pytanie, dobrą praktyką jest dodanie krótkiego wyjaśnienia dla społeczności, aby skorzystać (i uczyć się) z odpowiedzi
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);
    }
}

powinieneś po prostu poszukać pierwszej niezerowej cyfry po prawej stronie, a następnie użyć subString (a także sprawdzić, czy ciąg zawiera „.” oczywiście). w ten sposób nie będziesz musiał tworzyć tak wielu tymczasowych ciągów po drodze.
programista Androida

3

Spóźniona odpowiedź, ale ...

Powiedziałeś, że zdecydowałeś się przechowywać swoje liczby z podwójnym typem . Myślę, że może to być przyczyna problemu, ponieważ zmusza do przechowywania liczb całkowitych w liczbach podwójnych (a zatem utraty początkowej informacji o naturze wartości). Co o zapisywaniu numerów w instancji Number klasy (nadklasa Double i Integer) i poleganie na polimorfizmie w celu ustalenia poprawnego formatu każdej liczby?

Wiem, że refaktoryzacja całej części kodu może być niedopuszczalna z tego powodu, ale może wygenerować pożądany wynik bez dodatkowego kodu / rzutowania / parsowania.

Przykład:

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

}

Wytworzy następujące dane wyjściowe:

232
0.18
1237875192
4.58
0
1.2345


@Pyrolistic Czy możesz wyjaśnić nieco więcej swojego oświadczenia? Nie jest dla mnie całkiem jasne ... :)
Zauważyłem

2

Oto co wymyśliłem:

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

Prosty jeden liner, rzuca tylko int, jeśli naprawdę tego potrzebujesz


1
Powtórzenie tego, co powiedział Felix Edelmann w innym miejscu: spowoduje to utworzenie łańcucha niezależnego od ustawień regionalnych, co może nie zawsze być odpowiednie dla użytkownika.
JJ Brown

uczciwy punkt, w moim przypadku użycia nie był to problem, nie jestem teraz całkowicie pewien, ale myślę, że można użyć String.format (z poszukiwanymi ustawieniami narodowymi) zamiast valueOf
keisar

2

Sformatuj cenę z grupowaniem, zaokrąglaniem, bez zbędnych zer (podwójnie).

Zasady:

  1. Brak zer na końcu ( 2.0000 = 2; 1.0100000 = 1.01)
  2. Maksymalnie dwie cyfry po punkcie ( 2.010 = 2.01; 0.20 = 0.2)
  3. Zaokrąglanie po drugiej cyfrze po punkcie ( 1.994 = 1.99; 1.995 = 2; 1.006 = 1.01; 0.0006 -> 0)
  4. Zwroty 0(null/-0 = 0 )
  5. Dodaje $(= $56/-$56 )
  6. Grupowanie ( 101101.02 = $101,101.02)

Więcej przykładów:

-99.985 = -$99.99

10 = $10

10.00 = $10

20.01000089 = $20.01

Napisane w Kotlinie jako zabawne rozszerzenie Double (przyczyna używana w Androidzie), ale można je łatwo przekonwertować na Javę, ponieważ zastosowano klasy 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)
    }
}

Jak używać:

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

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

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

Musiałem skorzystać z tego powodu, d == (long)dże naruszyłem raport sonaru


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

jest to wynik niektórych testów:

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

A co z 1.23450000?
Alex78191

1,23450000 => 1,23
Ahmed Mihoub

0

Oto dwa sposoby na osiągnięcie tego. Po pierwsze, krótszy (i prawdopodobnie lepszy) sposób:

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

A oto dłuższy i prawdopodobnie gorszy sposób:

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
czasami, gdy upraszczasz wszystko, kod jest bardziej złożony i mniej zoptymalizowany ... ale tak, możesz użyć wielu wbudowanych funkcji API ...
programista Androida

1
Powinieneś zacząć od prostej, a kiedy już ustalisz, że masz problem z wydajnością, wtedy i tylko wtedy powinieneś zoptymalizować. Kod jest po to, by człowiek czytał w kółko. Sprawienie, by działało szybko, jest drugorzędne. Nieużywając standardowego interfejsu API, gdy tylko jest to możliwe, istnieje większe prawdopodobieństwo wprowadzenia błędów i utrudnia to zmianę w przyszłości.
Pirolistyczny

3
Argumentowałbym, że kod, który piszesz w ten sposób, NIE będzie szybszy. JVM jest bardzo inteligentny i tak naprawdę nie wiesz, jak szybko lub wolno jest coś, dopóki go nie profilujesz. Problemy z wydajnością można wykryć i naprawić, gdy stają się problemem. Nie należy przedwcześnie go optymalizować. Napisz kod, aby ludzie mogli go czytać, a nie jak wyobrażasz sobie, że maszyna go uruchomi. Gdy stanie się to problemem z wydajnością, przepisz kod za pomocą profilera.
Pirolistyczny

2
Ktoś inny zredagował odpowiedź, aby poprawić formatowanie kodu. Sprawdzałem kilkadziesiąt zmian do zatwierdzenia i zamierzałem zatwierdzić ich edycję tutaj, ale zmiany były niespójne, więc je naprawiłem. Poprawiłem także gramatykę fragmentów tekstu.
Steve Vinoski,

1
Nie rozumiem. Jeśli powiedziałeś, że formatowanie nie miało znaczenia, dlaczego spędziłeś czas, aby go zmienić?
OrhanC1

0

W przypadku Kotlin możesz użyć rozszerzenia takiego jak:

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

0

Oto kolejna odpowiedź, która ma opcję dodawania dziesiętnego TYLKO JEŻELI dziesiętna nie była równa 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

Oto odpowiedź, która faktycznie działa (kombinacja różnych odpowiedzi tutaj)

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

1
nie zastąpiłeś POINT, na przykład „100.0” zostanie przekonwertowane na „100”.
VinceStyling,

jeśli (f == (int) f) zajmuje się tym.
Martin Klosi,

2
Nie działa na f = 9999999999.00
Dawood ibn Kareem

-4

Wiem, że to naprawdę stary wątek. Myślę jednak, że najlepszym sposobem na to jest jak poniżej:

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

Wynik:

3.456 something
3.456234523452 something
3.45 something
3.0 something

Jedynym problemem jest ostatni problem, w którym 0,0 nie jest usuwany. Ale jeśli jesteś w stanie z tym żyć, to działa najlepiej. % .2f zaokrągli go do ostatnich 2 cyfr dziesiętnych. Podobnie będzie z DecimalFormat. Jeśli potrzebujesz wszystkich miejsc dziesiętnych, ale nie zer końcowych, działa to najlepiej.


2
DecimalFormat w formacie „#. ##” nie zachowa dodatkowych 0, jeśli nie będą potrzebne: System.out.println(new java.text.DecimalFormat("#.##").format(1.0005));wydrukuje1
Aleks G

to mój punkt. Co jeśli chcesz wyświetlać 0.0005, jeśli taki istnieje? Zaokrąglisz go o 2 cyfry dziesiętne.
sethu

OP pyta, jak wydrukować wartości całkowite zapisane w liczbach podwójnych :)
Aleks G

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

Spowoduje to, że ciąg upuści cyfry 0.


1
To dobre rozwiązanie pytania, jeśli byliby zainteresowani jedynie zrzucaniem zer końcowych, to jak zmieniłbyś swój kod, aby przycinać także dziesiętną kropkę dziesiętną? tzn. „1.”
bakoyaro

29
Uważaj, twoje rozwiązanie zamieni 1000 na 1, co jest złe.
Aleks G
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.