Bagaimana memformat angka mengambang menjadi String tanpa desimal 0 yang tidak perlu?

496

Double 64-bit dapat mewakili integer +/- 2 53 dengan tepat

Mengingat fakta ini saya memilih untuk menggunakan tipe ganda sebagai tipe tunggal untuk semua tipe saya, karena integer terbesar saya tidak bertanda 32-bit.

Tapi sekarang saya harus mencetak bilangan bulat semu ini, tetapi masalahnya adalah mereka juga bercampur dengan ganda yang sebenarnya.

Jadi bagaimana cara mencetak ganda ini dengan baik di Jawa?

Saya sudah mencoba String.format("%f", value), yang dekat, kecuali saya mendapatkan banyak angka nol untuk nilai kecil.

Berikut ini contoh keluaran dari %f

232.00000000
0,18000000000
1237875192.0
4.5800000000
0,00000000
1.23450000

Yang saya inginkan adalah:

232
0,18
1237875192
4.58
0
1.2345

Tentu saya bisa menulis fungsi untuk memangkas angka-angka itu, tapi itu banyak kehilangan kinerja karena manipulasi String. Bisakah saya melakukan yang lebih baik dengan kode format lain?

EDIT

Jawaban oleh Tom E. dan Jeremy S. tidak dapat diterima karena keduanya secara acak berputar ke 2 tempat desimal. Harap mengerti masalahnya sebelum menjawab.

EDIT 2

Harap perhatikan bahwa String.format(format, args...)ini tergantung pada lokal (lihat jawaban di bawah).

Pirolistik
sumber
Jika semua yang Anda inginkan adalah bilangan bulat, mengapa tidak menggunakan yang lama? Anda mendapatkan lebih banyak di 2 ^ 63-1, tidak ada format canggung, dan kinerja yang lebih baik.
basszero
14
Karena beberapa nilai sebenarnya berlipat ganda
Pyrolistic
2
Beberapa kasus di mana masalah ini terjadi adalah bug diperbaiki di JDK 7: stackoverflow.com/questions/7564525/…
Pyrolistic
Apakah hanya saya atau JavaScript 100% lebih baik pada konversi angka ke string daripada Java?
Andy
System.out.println("YOUR STRING" + YOUR_DOUBLE_VARIABLE);
Shayan Amani

Jawaban:

399

Jika idenya adalah mencetak bilangan bulat yang disimpan sebagai ganda seolah-olah bilangan bulat, dan sebaliknya mencetak ganda dengan presisi minimum yang diperlukan:

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

Menghasilkan:

232
0.18
1237875192
4.58
0
1.2345

Dan tidak mengandalkan manipulasi string.

JasonD
sumber
9
Setuju, ini adalah jawaban yang buruk, jangan gunakan itu. Gagal bekerja dengan doublelebih besar dari nilai maksimum int. Bahkan dengan longitu masih akan gagal untuk jumlah besar. Selanjutnya ia akan mengembalikan sebuah String dalam bentuk eksponensial, misalnya "1.0E10", untuk nilai-nilai besar, yang mungkin bukan yang diinginkan penanya. Gunakan %falih-alih %sdi string format kedua untuk memperbaikinya.
jlh
26
OP secara eksplisit menyatakan bahwa mereka tidak ingin output diformat menggunakan %f. Jawabannya khusus untuk situasi yang dijelaskan, dan output yang diinginkan. OP menyarankan nilai maksimum mereka adalah 32-bit int unsigned, yang saya ambil untuk berarti bahwa intdapat diterima (unsigned tidak benar-benar ada di Jawa, dan tidak ada contoh bermasalah), tetapi mengubah intuntuk longadalah memperbaiki sepele jika situasinya berbeda.
JasonD
1
Di mana dalam pertanyaan yang dikatakan tidak seharusnya melakukan itu?
JasonD
6
String.format("%s",d)??? Bicara tentang overhead yang tidak perlu. Gunakan Double.toString(d). Sama untuk yang lain: Long.toString((long)d).
Andreas
15
Masalahnya adalah itu %stidak berfungsi dengan Lokal. Dalam bahasa Jerman, kami menggunakan "," bukannya "." dalam angka desimal. Sementara String.format(Locale.GERMAN, "%f", 1.5)mengembalikan "1.500000", String.format(Locale.GERMAN, "%s", 1.5)mengembalikan "1,5" - dengan ".", Yang salah dalam bahasa Jerman. Apakah ada versi "% s" yang bergantung pada lokal juga?
Felix Edelmann
414
new DecimalFormat("#.##").format(1.199); //"1.2"

Seperti yang ditunjukkan dalam komentar, ini bukan jawaban yang tepat untuk pertanyaan awal.
Yang mengatakan, ini adalah cara yang sangat berguna untuk memformat angka tanpa membuntuti nol.

Tom Esterez
sumber
16
Catatan penting di sini adalah bahwa 1.1 akan diformat dengan benar sebagai "1.1" tanpa nol tambahan.
Steve Pomeroy
53
Dan jika Anda menginginkan jumlah nol trailing tertentu (mis. Jika Anda mencetak jumlah uang) maka Anda dapat menggunakan '0' alih-alih '#' (yaitu DecimalFormat baru ("0,00"). Format (jumlah);) ini bukan yang diinginkan OP, tetapi mungkin berguna untuk referensi.
TJ Ellis
22
Ya, sebagai penulis asli dari pertanyaan ini, inilah jawaban SALAH. Lucu berapa banyak suara yang ada. Masalah dengan solusi ini adalah ia secara acak membulatkan ke 2 tempat desimal.
Pyrolistic
11
@Mazyod karena Anda selalu dapat melewati floating pointing dengan lebih banyak desimal daripada format. Itu adalah penulisan kode yang akan bekerja sebagian besar waktu tetapi tidak mencakup semua kasus tepi.
Pyrolistic
15
@ Patrolistik - IMHO, ada banyak upvotes karena, meskipun ini adalah solusi yang salah untuk Anda, itu adalah solusi yang tepat untuk 99% + dari mereka yang menemukan T&J ini: biasanya, beberapa digit terakhir dari double adalah "noise", yang mengacaukan output, mengganggu keterbacaan. Oleh karena itu programmer menentukan berapa banyak digit yang bermanfaat bagi orang yang membaca output, dan menentukan banyak. Situasi umum adalah akumulasi kesalahan matematika kecil, sehingga nilainya mungkin 12.000000034, tetapi lebih suka membulatkan ke 12, dan menampilkan kompak sebagai "12". Dan "12.340000056" => "12,34".
ToolmakerSteve
226
String.format("%.2f", value) ;
Jeremy Slade
sumber
13
Itu benar tetapi selalu mencetak angka nol meskipun tidak ada bagian pecahan. String.format ("%. 2f, 1.0005) mencetak 1,00 dan bukan 1. Apakah ada penentu format untuk tidak mencetak bagian fraksional jika tidak ada?
Emre Yazici
87
Turut memilih karena pertanyaannya adalah meminta untuk menghapus semua nol yang tertinggal dan jawaban ini akan selalu meninggalkan dua titik mengambang tanpa menjadi nol.
Zulaxia
DecimalFormat adalah trik yang bagus - meskipun saya akhirnya menggunakan yang ini untuk situasi saya (timer level permainan) karena angka nol yang tertinggal terlihat lebih baik.
Timothy Lee Russell
2
Saya pikir Anda dapat menangani nol trailing dengan benar dengan menggunakan g, bukan f.
Peter Ajtai
3
Saya menggunakan solusi ini dalam sistem produksi dengan "% .5f", dan itu benar-benar sangat buruk, jangan gunakan itu ... karena ia mencetak ini: 5.12E-4 bukannya 0,000512
hamish
87

Pendeknya:

Jika Anda ingin menghilangkan angka nol dan masalah Lokal, maka Anda harus menggunakan:

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

Penjelasan:

Mengapa jawaban lain tidak cocok untuk saya:

  • Double.toString()atau System.out.printlnatau FloatingDecimal.toJavaFormatStringmenggunakan notasi ilmiah jika dobel kurang dari 10 ^ -3 atau lebih besar dari atau sama dengan 10 ^ 7

    double myValue = 0.00000021d;
    String.format("%s", myvalue); //output: 2.1E-7
  • dengan menggunakan %f, presisi desimal default adalah 6, jika tidak, Anda dapat meng-hardcode-nya tetapi hasilnya nol tambahan yang ditambahkan jika Anda memiliki lebih sedikit desimal. Contoh:

    double myValue = 0.00000021d;
    String.format("%.12f", myvalue); //output: 0.000000210000
  • dengan menggunakan setMaximumFractionDigits(0);atau %.0fAnda menghapus presisi desimal, yang baik untuk bilangan bulat / rindu tetapi tidak untuk ganda

    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
  • dengan menggunakan DecimalFormat, Anda bergantung pada lokal. Di lokal Prancis, pemisah desimal adalah koma, bukan titik:

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

    Menggunakan lokal BAHASA INDONESIA memastikan Anda mendapatkan titik untuk pemisah desimal, di mana pun program Anda akan berjalan

Mengapa menggunakan 340 lalu untuk setMaximumFractionDigits?

Dua alasan:

  • setMaximumFractionDigitsmenerima integer tetapi implementasinya memiliki angka maksimum yang diizinkan DecimalFormat.DOUBLE_FRACTION_DIGITSyang sama dengan 340
  • Double.MIN_VALUE = 4.9E-324 jadi dengan 340 digit Anda yakin untuk tidak membulatkan presisi ganda dan longgar Anda
JBE
sumber
Ini tidak berfungsi untuk bilangan bulat, misalnya "2" menjadi "2."
kap
Terima kasih, saya telah memperbaiki jawabannya dengan menggunakan pola 0alih-alih#.
JBE
Anda tidak menggunakan konstanta DecimalFormat.DOUBLE_FRACTION_DIGITStetapi Anda menggunakan nilai 340, yang kemudian Anda berikan komentar untuk menunjukkan bahwa itu sama dengan DecimalFormat.DOUBLE_FRACTION_DIGITS. Mengapa tidak menggunakan konstanta saja ???
Maarten Bodewes
1
Karena atribut ini bukan untuk umum ... ini adalah "paket ramah"
JBE
4
Terima kasih! Sebenarnya, jawaban ini adalah satu-satunya yang benar-benar cocok dengan semua persyaratan yang disebutkan dalam pertanyaan - tidak menunjukkan nol yang tidak perlu, tidak membulatkan angka, dan bergantung pada lokal. Bagus!
Felix Edelmann
26

Kenapa tidak:

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

Ini harus bekerja dengan nilai ekstrim yang didukung oleh Double. Hasil:

0.12
12
12.144252
0
Valeriu Paloş
sumber
2
Saya lebih suka jawaban ini dimana kita tidak perlu melakukan konversi jenis.
Jeff T.
Penjelasan singkat: "%s"pada dasarnya panggilan d.toString()tetapi tidak bekerja dengan intatau jika d==null!
Neph
24

Di komputer saya, fungsi berikut kira-kira 7 kali lebih cepat daripada fungsi yang disediakan oleh jawaban JasonD , karena itu menghindari String.format:

public static String prettyPrint(double d) {
  int i = (int) d;
  return d == i ? String.valueOf(i) : String.valueOf(d);
}
Rok Strniša
sumber
1
Hmm, ini tidak memperhitungkan lokal, tetapi JasonD juga tidak.
TWiStErRob
22

2 sen saya:

if(n % 1 == 0) {
    return String.format(Locale.US, "%.0f", n));
} else {
    return String.format(Locale.US, "%.1f", n));
}
Fernando Gallego
sumber
2
Atau adil return String.format(Locale.US, (n % 1 == 0 ? "%.0f" : "%.1f"), n);.
MC Emperor
gagal ketika 23.00123 == 23.00
aswzen
11

Tidak, sudahlah.

Kehilangan kinerja karena manipulasi String adalah nol.

Dan inilah kode untuk memotong akhir setelahnya %f

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

    return number.replaceAll("\\.?0*$", "");
}
Pirolistik
sumber
7
Saya downvoted karena solusi Anda bukan cara terbaik untuk pergi. Lihatlah String.format. Anda perlu menggunakan jenis format yang benar, float dalam contoh ini. Lihatlah jawaban saya di atas.
jjnguy
6
Saya memilih karena saya mengalami masalah yang sama, dan tidak ada seorang pun di sini yang memahami masalah tersebut.
Obay
1
Turun sebagai DecimalFormat yang disebutkan dalam posting Tom adalah persis apa yang Anda cari. Ini strip nol cukup efektif.
Steve Pomeroy
4
Untuk yang di atas, mungkin dia ingin memotong nol TANPA pembulatan? PS @ Patrolistic, pasti Anda bisa menggunakan number.replaceAll (".? 0 * $", ""); (setelah mengandung (".") tentu saja)
Rehno Lindeque
1
Oke, lalu bagaimana Anda bisa mencapai tujuan saya dengan DecimalFormat?
Pirolistik
8

Gunakan a DecimalFormatdansetMinimumFractionDigits(0)

vlazzle
sumber
Saya akan menambahkan setMaximumFractionDigits(2)dan setGroupingUsed(false)(OP tidak menyebutkannya tetapi dari contoh tampaknya diperlukan). Juga, test case kecil tidak sakit sejak sepele dalam kasus ini. Namun, karena saya pikir ini solusi paling sederhana, sebuah upvote adalah sebuah upvote :)
acrespo
6
if (d == Math.floor(d)) {
    return String.format("%.0f", d);
} else {
    return Double.toString(d);
}
fop6316
sumber
1
Saya pikir saya akan mengikuti Anda dalam hal ini: D
aeracode
5

Harap perhatikan bahwa String.format(format, args...)ini bergantung pada lokal karena memformat menggunakan lokal default pengguna, yaitu, mungkin dengan koma dan bahkan spasi di dalam seperti 123 456.789 atau 123.456.789 , yang mungkin tidak persis seperti yang Anda harapkan.

Anda mungkin lebih suka menggunakan String.format((Locale)null, format, args...).

Sebagai contoh,

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

cetakan

123456,789000
123456,789000
123456.789000

dan inilah yang akan String.format(format, args...)dilakukan di berbagai negara.

EDIT Ok, karena telah ada diskusi tentang formalitas:

    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
sumber
1
Anda harus menambahkan ini sebagai komentar pada jawaban yang diterima
Pyrolistic
Komentar tidak mengizinkan panjang atau format dari addendum ini. Karena itu menambahkan informasi yang mungkin berguna, saya pikir itu harus dibiarkan apa adanya daripada dihapus.
Terry Jan Reedy
5

Saya membuat DoubleFormatteruntuk secara efisien mengkonversi sejumlah besar nilai ganda ke String yang bagus / rapi:

double horribleNumber = 3598945.141658554548844; 
DoubleFormatter df = new DoubleFormatter(4,6); //4 = MaxInteger, 6 = MaxDecimal
String beautyDisplay = df.format(horribleNumber);
  • Jika bagian integer dari V memiliki lebih dari MaxInteger => tampilan V dalam format ilmuwan (1.2345e + 30) sebaliknya ditampilkan dalam format normal 124.45678.
  • MaxDecimal menentukan jumlah digit desimal (trim dengan pembulatan bankir)

Berikut kodenya:

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

Catatan: Saya menggunakan 2 fungsi dari pustaka GUAVA. Jika Anda tidak menggunakan GUAVA, kode sendiri:

/**
 * 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();
}
Hai
sumber
1
Jika Anda tahu ketepatannya, maka gunakan BigDecimal. Lihat docs.oracle.com/javase/1.5.0/docs/api/java/math/…
Pyrolistic
5

Yang ini akan menyelesaikan pekerjaan dengan baik, saya tahu topiknya sudah tua, tetapi saya masih berjuang dengan masalah yang sama sampai saya tiba di sini. Saya harap seseorang menemukannya bermanfaat.

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

new DecimalFormat("00.#").format(2.236)
//out =02.2
  1. 0 untuk jumlah digit minimum
  2. Render # digit
Hossein Hajizadeh
sumber
sementara ini dapat memberikan solusi untuk pertanyaan, itu adalah praktik yang baik untuk menambahkan penjelasan singkat bagi masyarakat untuk mendapatkan manfaat (dan belajar) dari jawabannya
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);
    }
}
kamal
sumber
Anda seharusnya hanya mencari non-nol-digit pertama dari kanan dan kemudian menggunakan subString (dan juga memverifikasi bahwa string berisi "." Tentu saja). dengan cara ini, Anda tidak akan menciptakan begitu banyak string sementara di jalan.
pengembang android
3

Jawaban terlambat tapi ...

Anda bilang Anda memilih untuk menyimpan nomor Anda dengan tipe ganda . Saya pikir ini bisa menjadi akar masalah karena memaksa Anda untuk menyimpan bilangan bulat menjadi ganda (dan karena itu kehilangan informasi awal tentang sifat nilai). Bagaimana dengan menyimpan nomor Anda dalam instance kelas Number (superclass dari Double dan Integer) dan mengandalkan polimorfisme untuk menentukan format yang benar dari setiap nomor?

Saya tahu itu mungkin tidak dapat diterima untuk refactor seluruh bagian dari kode Anda karena itu tetapi bisa menghasilkan output yang diinginkan tanpa kode tambahan / casting / parsing.

Contoh:

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

}

Akan menghasilkan output berikut:

232
0.18
1237875192
4.58
0
1.2345
Tutul
sumber
javascript membuat pilihan yang sama :)
Pyrolistic
@ Patrolistik Bisakah Anda menjelaskan lebih banyak tentang pernyataan Anda? Tidak begitu jelas bagi saya ... :)
Terlihat
2

Inilah yang saya pikirkan:

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

Sederhana satu liner, hanya dicetak ke int jika benar-benar perlu

keisar
sumber
1
Mengulangi apa yang dikatakan Felix Edelmann di tempat lain: ini akan membuat string independen-lokal, yang mungkin tidak selalu sesuai untuk pengguna.
JJ Brown
titik adil, untuk kasus penggunaan saya ini bukan masalah, saya tidak sepenuhnya yakin sekarang tapi saya pikir orang bisa menggunakan String.format (dengan Locale yang diinginkan) alih-alih valueOf
keisar
2

Format harga dengan pengelompokan, pembulatan, tidak ada nol yang tidak perlu (ganda).

Aturan:

  1. Tidak ada nol di akhir ( 2.0000 = 2; 1.0100000 = 1.01)
  2. Dua digit maksimum setelah satu titik ( 2.010 = 2.01; 0.20 = 0.2)
  3. Pembulatan setelah digit ke-2 setelah titik ( 1.994 = 1.99; 1.995 = 2; 1.006 = 1.01; 0.0006 -> 0)
  4. Pengembalian 0( null/-0 = 0)
  5. Tambah $( = $56/-$56)
  6. Pengelompokan ( 101101.02 = $101,101.02)

Lebih banyak contoh:

-99.985 = -$99.99

10 = $10

10.00 = $10

20.01000089 = $20.01

Ditulis dalam Kotlin sebagai ekstensi menyenangkan Double (sebab digunakan di Android), tetapi dapat dikonversi ke Java dengan mudah, menyebabkan kelas java digunakan.

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

Cara Penggunaan:

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

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

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

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

Saya harus menggunakan penyebab d == (long)dini memberi saya pelanggaran dalam laporan sonar

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

ini adalah hasil dari beberapa tes:

4.30     => 4.3
4.39     => 4.39  // Choose format.setRoundingMode(RoundingMode.UP) to get 4.4
4.000000 => 4
4        => 4
Ahmed Mihoub
sumber
Bagaimana dengan 1.23450000?
Alex78191
1.23450000 => 1.23
Ahmed Mihoub
0

Berikut adalah dua cara untuk mencapainya. Pertama, cara yang lebih pendek (dan mungkin lebih baik):

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

Dan inilah cara yang lebih panjang dan mungkin lebih buruk:

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;
  }
pengembang android
sumber
1
terkadang, ketika Anda membuat segalanya lebih sederhana, kode di belakang lebih kompleks dan kurang dioptimalkan ... tapi ya, Anda dapat menggunakan banyak fungsi API bawaan ...
pengembang android
1
Anda harus mulai dengan yang sederhana dan sekali Anda telah menentukan Anda memiliki masalah kinerja, maka dan hanya kemudian Anda harus mengoptimalkan. Kode bagi manusia untuk dibaca berulang kali. Membuatnya berjalan cepat adalah nomor dua. Dengan tidak menggunakan API standar bila memungkinkan Anda lebih mungkin untuk memperkenalkan bug dan hanya membuatnya lebih sulit untuk diubah di masa depan.
Pirolistik
3
Saya berpendapat kode yang Anda tulis seperti itu TIDAK akan menjadi lebih cepat. JVM sangat cerdas dan Anda tidak benar-benar tahu seberapa cepat atau lambat sesuatu sampai Anda memprofilkannya. Masalah kinerja dapat dideteksi dan diperbaiki ketika itu menjadi masalah. Anda tidak harus mengoptimalkannya sebelum waktunya. Tulis kode untuk dibaca orang, bukan untuk bagaimana Anda membayangkan mesin akan menjalankannya. Setelah itu menjadi masalah kinerja, tulis ulang kode dengan profiler.
Pirolistik
2
Orang lain mengedit jawaban untuk memperbaiki pemformatan kode. Saya sedang meninjau beberapa lusin suntingan untuk persetujuan dan akan menyetujui suntingan mereka di sini, tetapi suntingan tidak konsisten sehingga saya memperbaikinya. Saya juga meningkatkan tata bahasa dari potongan teks.
Steve Vinoski
1
Saya tidak mengerti. Jika Anda mengatakan format tidak penting, mengapa Anda menghabiskan waktu untuk mengubahnya kembali?
OrhanC1
0

Untuk Kotlin Anda dapat menggunakan ekstensi seperti:

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

Berikut jawaban lain yang memiliki opsi untuk menambahkan desimal SAJA JIKA desimal tidak nol.

   /**
     * 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
sumber
-1

Inilah jawaban yang benar-benar berfungsi (kombinasi jawaban berbeda di sini)

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
sumber
1
Anda tidak mengganti POINT, misalnya, "100.0" akan dikonversi menjadi "100."
VinceStyling
jika (f == (int) f) mengatasinya.
Martin Klosi
2
Gagal pada f = 9999999999.00
Dawood ibn Kareem
-4

Saya tahu ini adalah utas yang sangat lama .. Tapi saya pikir cara terbaik untuk melakukan ini adalah sebagai berikut:

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

Keluaran:

3.456 something
3.456234523452 something
3.45 something
3.0 something

Satu-satunya masalah adalah yang terakhir di mana .0 tidak dihapus. Tetapi jika Anda dapat hidup dengan itu maka ini bekerja paling baik. % .2f akan membulatkannya ke 2 angka desimal terakhir. Begitu juga DecimalFormat. Jika Anda membutuhkan semua tempat desimal tetapi bukan nol yang tertinggal maka ini bekerja paling baik.

sethu
sumber
2
DecimalFormat dengan format "#. ##" tidak akan menyimpan tambahan 0 jika tidak diperlukan: System.out.println(new java.text.DecimalFormat("#.##").format(1.0005));akan dicetak1
Aleks G
Itu maksudku. Bagaimana jika Anda ingin 0,0005 ditampilkan jika ada. Anda akan membulatkannya 2 angka desimal.
sethu
OP bertanya bagaimana mencetak nilai integer yang disimpan dalam ganda :)
Aleks G
-8
String s = "1.210000";
while (s.endsWith("0")){
    s = (s.substring(0, s.length() - 1));
}

Ini akan membuat string untuk menjatuhkan tailing 0-s.

Kadi
sumber
1
Ini adalah solusi yang bagus untuk pertanyaan itu, jika mereka hanya tertarik untuk membuntuti nol yang dibuang, bagaimana Anda akan mengubah kode Anda untuk juga memotong titik desimal yang tertinggal? yaitu "1."
bakoyaro
29
Hati-hati, solusi Anda akan mengubah 1000 menjadi 1, yang salah.
Aleks G