Cara membulatkan angka ke n tempat desimal di Jawa


1259

Apa yang saya inginkan adalah metode untuk mengkonversi ganda menjadi string yang dibulatkan menggunakan metode setengah naik - yaitu jika desimal yang dibulatkan adalah 5, selalu dibulatkan ke angka berikutnya. Ini adalah metode standar pembulatan yang kebanyakan orang harapkan dalam kebanyakan situasi.

Saya juga ingin hanya digit signifikan yang ditampilkan - yaitu tidak boleh ada nol tambahan.

Saya tahu salah satu metode melakukan ini adalah dengan menggunakan String.formatmetode ini:

String.format("%.5g%n", 0.912385);

pengembalian:

0.91239

yang hebat, namun selalu menampilkan angka dengan 5 tempat desimal meskipun tidak signifikan:

String.format("%.5g%n", 0.912300);

pengembalian:

0.91230

Metode lain adalah dengan menggunakan DecimalFormatter:

DecimalFormat df = new DecimalFormat("#.#####");
df.format(0.912385);

pengembalian:

0.91238

Namun seperti yang Anda lihat ini menggunakan pembulatan setengah-setengah. Itu akan dibulatkan ke bawah jika angka sebelumnya genap. Yang saya suka adalah ini:

0.912385 -> 0.91239
0.912300 -> 0.9123

Apa cara terbaik untuk mencapai ini di Jawa?

Jawaban:


751

Gunakan setRoundingMode, aturRoundingMode secara eksplisit untuk menangani masalah Anda dengan babak setengah genap, lalu gunakan pola format untuk output yang Anda perlukan.

Contoh:

DecimalFormat df = new DecimalFormat("#.####");
df.setRoundingMode(RoundingMode.CEILING);
for (Number n : Arrays.asList(12, 123.12345, 0.23, 0.1, 2341234.212431324)) {
    Double d = n.doubleValue();
    System.out.println(df.format(d));
}

memberikan output:

12
123.1235
0.23
0.1
2341234.2125

EDIT : Jawaban asli tidak membahas keakuratan nilai ganda. Itu bagus jika Anda tidak terlalu peduli apakah itu naik atau turun. Tetapi jika Anda ingin pembulatan akurat, maka Anda perlu memperhitungkan keakuratan nilai yang diharapkan. Nilai floating point memiliki representasi biner secara internal. Itu berarti bahwa nilai seperti 2,7735 sebenarnya tidak memiliki nilai tepat itu secara internal. Itu bisa sedikit lebih besar atau sedikit lebih kecil. Jika nilai internal sedikit lebih kecil, maka tidak akan bulat hingga 2,7740. Untuk memperbaiki situasi itu, Anda harus menyadari keakuratan nilai yang Anda kerjakan, dan menambah atau mengurangi nilai itu sebelum pembulatan. Misalnya, ketika Anda tahu bahwa nilai Anda akurat hingga 6 digit, lalu untuk membulatkan nilai setengah jalan, tambahkan akurasi itu ke nilai:

Double d = n.doubleValue() + 1e-6;

Untuk membulatkannya, kurangi akurasinya.


9
Ini mungkin solusi terbaik yang disajikan sejauh ini. Alasan saya tidak melihat fasilitas ini ketika saya pertama kali melihat kelas DecimalFormat adalah bahwa itu hanya diperkenalkan di Java 1.6. Sayangnya saya dibatasi untuk menggunakan 1,5 tetapi akan berguna untuk mengetahui masa depan.
Alex Spurling

1
Saya mencoba ini dengan "#.##":, pembulatan HALF_UP. 256.335f-> "256.33"... (contoh berasal dari komentar hingga jawaban @ asterite).
bigstones

6
Harap berhati-hati karena DecimalFormat bergantung pada konfigurasi lokal Anda saat ini, Anda mungkin tidak mendapatkan titik sebagai pemisah. Saya pribadi lebih suka jawaban Asterite di bawah ini
Gomino

1
Perlu diketahui juga bahwa Anda tidak boleh mengharapkan DecimalFormat aman-aman. Sesuai dokumen Java : Format desimal umumnya tidak disinkronkan. Disarankan untuk membuat instance format terpisah untuk setiap utas. Jika beberapa utas mengakses suatu format secara bersamaan, itu harus disinkronkan secara eksternal.
CGK

1
bagaimana cara membuatnya sehingga melakukan pembulatan yang tepat sehingga tidak akan membulatkan 0,0004 ke 0,001

471

Dengan asumsi valueadalah double, Anda dapat melakukan:

(double)Math.round(value * 100000d) / 100000d

Itu untuk presisi 5 digit. Jumlah nol menunjukkan jumlah desimal.


71
UPDATE: Saya baru saja mengkonfirmasi bahwa melakukan ini IS WAY lebih cepat daripada menggunakan DecimalFormat. Saya mengulang menggunakan DecimalFormat 200 kali, dan metode ini. DecimalFormat membutuhkan 14 ms untuk menyelesaikan 200 loop, metode ini membutuhkan kurang dari 1 ms. Seperti yang saya duga, ini lebih cepat. Jika Anda dibayar oleh siklus jam, inilah yang harus Anda lakukan. Saya terkejut Chris Cudmore bahkan akan mengatakan apa yang dia katakan jujur. mengalokasikan objek selalu lebih mahal daripada casting primitif dan menggunakan metode statis (Math.round () dibandingkan dengan decimalFormat.format ()).
Andi Jay

99
Teknik ini gagal di lebih dari 90% kasus. -1.
Marquis of Lorne

25
Memang, ini gagal: Math.round(0.1 * Math.pow(10,20))/Math.pow(10,20) == 0.09223372036854775.
Robert Tupelo-Schneck

54
Berhati-hatilah saat menggunakan metode ini (atau pembulatan titik apung). Gagal untuk sesuatu yang sesederhana 265.335. Hasil antara 265.335 * 100 (presisi 2 digit) adalah 26533.499999999996. Ini berarti akan dibulatkan ke 265,33. Hanya ada masalah inheren ketika mengkonversi dari angka floating point ke angka desimal nyata. Lihat jawaban EJP di sini di stackoverflow.com/a/12684082/144578
Sebastiaan van den Broek

6
@SebastiaanvandenBroek: Wow, saya tidak pernah tahu semudah itu untuk mendapatkan jawaban yang salah. Namun, jika seseorang bekerja dengan angka yang tidak tepat, ia harus menyadari bahwa nilai apa pun tidak tepat . 265.335sangat berarti 265.335 += tolerance, di mana toleransi bergantung pada operasi sebelumnya dan kisaran nilai input. Kami tidak tahu nilai sebenarnya dan tepat. Pada nilai-nilai tepi, baik jawaban ini bisa dibilang benar. Jika kita harus tepat, kita seharusnya tidak bekerja ganda. Di failsini bukan dalam mengkonversi kembali menjadi dua kali lipat. Itu dalam pemikiran OP dia bisa mengandalkan masuk 265.335sebagai persis seperti itu.
ToolmakerSteve

191
new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);

akan membuat Anda BigDecimal. Untuk mendapatkan string dari itu, hanya menyebut bahwa BigDecimal's toStringmetode, atautoPlainString metode untuk Java 5 + untuk format string polos.

Program sampel:

package trials;
import java.math.BigDecimal;

public class Trials {

    public static void main(String[] args) {
        int yourScale = 10;
        System.out.println(BigDecimal.valueOf(0.42344534534553453453-0.42324534524553453453).setScale(yourScale, BigDecimal.ROUND_HALF_UP));
    }

38
Itu solusi pilihan saya. Bahkan lebih pendek: BigDecimal.valueOf (doubleVar) .setScale (yourScaleHere, BigDecimal.ROUND_HALF_UP); BigDecimal.valueOf (double val) sebenarnya memanggil Double.toString () di bawah tenda;)
Etienne Neveu

4
Bagus. Jangan mengambil jalan pintas dan gunakan new BigDecimal(doubleVar)karena Anda dapat mengalami masalah dengan pembulatan poin mengambang
Edd

8
@ Ed, yang menarik, masalah pembulatan terjadi dalam kasus SebastiaanvandenBroek menyebutkan komentar untuk jawaban asterit. double val = 265.335;, BigDecimal.valueOf(val).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString();=> 265.34, tetapi (new BigDecimal(val)).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString();=> 265.33.
ToolmakerSteve

7
@ToolmakerSteve Itu karena menggunakan BigDecimal baru dengan ganda mengambil nilai ganda langsung dan upaya untuk menggunakannya untuk membuat BigDecimal, sedangkan ketika menggunakan BigDecimal.valueOf atau bentuk tostring mem-parsing ke string terlebih dahulu (representasi yang lebih tepat) sebelum konversi .
MetroidFan2002

116

Anda juga dapat menggunakan

DecimalFormat df = new DecimalFormat("#.00000");
df.format(0.912385);

untuk memastikan Anda memiliki 0 yang tertinggal.


25
Saya percaya salah satu tujuan dari pertanyaan itu adalah " tidak boleh ada nol tambahan".
Lunchbox

7
Untuk pertanyaan ini, op tidak menginginkan angka nol, tetapi inilah yang saya inginkan. Jika Anda memiliki daftar angka dengan 3 tempat desimal, Anda ingin semuanya memiliki angka yang sama walaupun itu angka 0.
Tom Kincaid

Anda lupa menentukanRoundingMode.
IgorGanapolsky

1
@IgorGanapolsky secara default Decimal modemenggunakanRoundingMode.HALF_EVEN.
EndermanAPM

87

Seperti beberapa orang lain catat, jawaban yang benar adalah menggunakan salah satu DecimalFormatatau BigDecimal. Floating-point tidak memiliki tempat desimal sehingga Anda tidak dapat membulatkan / memotong ke jumlah tertentu di tempat pertama. Anda harus bekerja dalam radix desimal, dan itulah yang dilakukan kedua kelas itu.

Saya memposting kode berikut sebagai contoh balasan untuk semua jawaban di utas ini dan memang di seluruh StackOverflow (dan di tempat lain) yang merekomendasikan perkalian diikuti oleh pemotongan diikuti oleh pembagian. Merupakan kewajiban para pendukung teknik ini untuk menjelaskan mengapa kode berikut menghasilkan output yang salah di lebih dari 92% kasus.

public class RoundingCounterExample
{

    static float roundOff(float x, int position)
    {
        float a = x;
        double temp = Math.pow(10.0, position);
        a *= temp;
        a = Math.round(a);
        return (a / (float)temp);
    }

    public static void main(String[] args)
    {
        float a = roundOff(0.0009434f,3);
        System.out.println("a="+a+" (a % .001)="+(a % 0.001));
        int count = 0, errors = 0;
        for (double x = 0.0; x < 1; x += 0.0001)
        {
            count++;
            double d = x;
            int scale = 2;
            double factor = Math.pow(10, scale);
            d = Math.round(d * factor) / factor;
            if ((d % 0.01) != 0.0)
            {
                System.out.println(d + " " + (d % 0.01));
                errors++;
            }
        }
        System.out.println(count + " trials " + errors + " errors");
    }
}

Output dari program ini:

10001 trials 9251 errors

EDIT: Untuk membahas beberapa komentar di bawah ini saya redid bagian modulus dari loop tes menggunakan BigDecimaldan new MathContext(16)untuk operasi modulus sebagai berikut:

public static void main(String[] args)
{
    int count = 0, errors = 0;
    int scale = 2;
    double factor = Math.pow(10, scale);
    MathContext mc = new MathContext(16, RoundingMode.DOWN);
    for (double x = 0.0; x < 1; x += 0.0001)
    {
        count++;
        double d = x;
        d = Math.round(d * factor) / factor;
        BigDecimal bd = new BigDecimal(d, mc);
        bd = bd.remainder(new BigDecimal("0.01"), mc);
        if (bd.multiply(BigDecimal.valueOf(100)).remainder(BigDecimal.ONE, mc).compareTo(BigDecimal.ZERO) != 0)
        {
            System.out.println(d + " " + bd);
            errors++;
        }
    }
    System.out.println(count + " trials " + errors + " errors");
}

Hasil:

10001 trials 4401 errors

8
Kuncinya adalah bahwa dalam semua kesalahan 9251 Anda, hasil yang dicetak masih benar.
Didier L

7
@Dierier Tidak mengejutkan saya. Saya sangat beruntung melakukan 'Metode Numerik' sebagai kursus komputasi pertama saya dan diperkenalkan pada awal apa yang bisa dan tidak bisa dilakukan oleh floating-point. Sebagian besar programmer cukup samar tentangnya.
Marquis of Lorne

15
Yang Anda lakukan hanyalah menyangkal bahwa floating tidak mewakili banyak nilai desimal, yang saya harap kita semua mengerti. Bukan berarti pembulatan menyebabkan masalah. Seperti yang Anda akui, angka-angka masih dicetak seperti yang diharapkan.
Peter Lawrey

8
Tes Anda rusak, ambil putaran () dan tes gagal 94% dari waktu. ideone.com/1y62CY mencetak 100 trials 94 errorsAnda harus mulai dengan tes yang lulus, dan menunjukkan bahwa memperkenalkan pembulatan akan merusak tes.
Peter Lawrey

6
Penolakan, disangkal di sini. Menggunakan Math.round untuk rentang ini doublekarena tidak ada kesalahan ideone.com/BVCHh3
Peter Lawrey

83

Misalkan Anda punya

double d = 9232.129394d;

kamu bisa menggunakan BigDecimal

BigDecimal bd = new BigDecimal(d).setScale(2, RoundingMode.HALF_EVEN);
d = bd.doubleValue();

atau tanpa BigDecimal

d = Math.round(d*100)/100.0d;

dengan kedua solusi d == 9232.13


2
Saya pikir ini adalah solusi terbaik untuk pengguna Java 1.5 (dan di bawah). Satu komentar, jangan gunakan mode pembulatan HALF_EVEN karena memiliki perilaku yang berbeda untuk angka ganjil dan genap (2,5 putaran ke 2 sementara 5,5 putaran ke 6, misalnya), kecuali jika ini yang Anda inginkan.
IcedDante

4
Solusi pertama benar: yang kedua tidak berhasil. Lihat di sini untuk bukti.
Marquis of Lorne

1
@ EJP: Bahkan solusi pertama dengan RoundingMode.HALF_UPsalah. Cobalah 1.505. Cara yang benar adalah menggunakan BigDecimal.valueOf(d).
Matthias Braun

Matthias Braun, solusinya baik-baik saja, maka 31 up .. 1,505 desimal disimpan dalam floating point double sebagai 1,50499998 jika Anda ingin mengambil 1,505 dan mengonversi dari ganda ke desimal, maka Anda harus mengonversinya menjadi Double.toString (x) terlebih dahulu kemudian letakkan di BigDecimal (), tapi itu sangat lambat, dan mengalahkan tujuan menggunakan ganda untuk kecepatan di tempat pertama.
hamish

1
Lari satu putaran 100k dengan BigDecimal (ambil 225 ms) dan Math.round (2 ms) jalan dan inilah waktunya ... Waktu Diambil: 225 mili detik untuk mengonversi menggunakan ke: 9232.13 Waktu Diambil: 2 mili detik untuk mengkonversi ke : 9232.13 techiesinfo.com
user1114134

59

Anda bisa menggunakan kelas DecimalFormat.

double d = 3.76628729;

DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal =  Double.valueOf(newFormat.format(d));

Ada alasan mengapa Double.valueOf()dipilih Double.parseDouble()? The valueOf()Metode mengembalikan Doubleobjek, sementara parseDouble()akan kembali doubleprimitif. Dengan cara kode saat ini ditulis, Anda juga menerapkan auto-unboxing untuk mengembalikannya ke primitif yang twoDoublediharapkan oleh variabel Anda , operasi bytecode tambahan. Saya akan mengubah jawaban untuk digunakan parseDouble()sebagai gantinya.
ecbrodie

Double.parseDouble()butuh Stringinput.
Ryde

38

Bagaimana Cara Nyata Java memposting solusi ini, yang juga kompatibel untuk versi sebelum Java 1.6.

BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();

33
double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;

2
ya ini persis apa yang dilakukan math.round untuk angka positif, tetapi apakah Anda sudah mencoba ini dengan angka negatif? orang menggunakan matematika. Temukan solusi lain untuk juga mencakup kasus angka negatif.
hamish

Catatan: Math.floor(x + 0.5)danMath.round(x)
Peter Lawrey

30

@ Milhous: format desimal untuk pembulatan sangat bagus:

Anda juga dapat menggunakan

DecimalFormat df = new DecimalFormat("#.00000");
df.format(0.912385);

untuk memastikan Anda memiliki 0 yang tertinggal.

Saya akan menambahkan bahwa metode ini sangat baik dalam menyediakan numerik, mekanisme pembulatan aktual - tidak hanya secara visual, tetapi juga saat memproses.

Hipotetis: Anda harus menerapkan mekanisme pembulatan ke dalam program GUI. Untuk mengubah keakuratan / ketepatan hasil keluaran cukup ubah format tanda sisipan (yaitu dalam kurung). Maka:

DecimalFormat df = new DecimalFormat("#0.######");
df.format(0.912385);

akan kembali sebagai output: 0.912385

DecimalFormat df = new DecimalFormat("#0.#####");
df.format(0.912385);

akan kembali sebagai output: 0.91239

DecimalFormat df = new DecimalFormat("#0.####");
df.format(0.912385);

akan kembali sebagai output: 0.9124

[EDIT: juga jika format caret seperti itu ("# 0. ############") dan Anda memasukkan desimal, misalnya 3.1415926, demi argumen, DecimalFormat tidak menghasilkan sampah apa pun ( misalnya membuntuti nol) dan akan kembali: 3.1415926.. jika Anda cenderung. Memang, ini sedikit bertele-tele untuk menyukai beberapa dev - tapi hei, punya jejak memori rendah selama pemrosesan dan sangat mudah untuk diterapkan.]

Jadi pada dasarnya, keindahan DecimalFormat adalah bahwa ia secara bersamaan menangani tampilan string - serta tingkat set pembulatan presisi. Ergo: Anda mendapatkan dua manfaat dengan harga satu implementasi kode. ;)


2
Jika Anda benar-benar ingin angka desimal untuk perhitungan (dan tidak hanya untuk output), jangan gunakan format floating point berbasis biner seperti double. Gunakan BigDecimal atau format berbasis desimal lainnya.
Paŭlo Ebermann

20

Berikut ini adalah ringkasan dari apa yang dapat Anda gunakan jika Anda ingin hasilnya sebagai String:

  1. DecimalFormat # setRoundingMode () :

    DecimalFormat df = new DecimalFormat("#.#####");
    df.setRoundingMode(RoundingMode.HALF_UP);
    String str1 = df.format(0.912385)); // 0.91239
  2. BigDecimal # setScale ()

    String str2 = new BigDecimal(0.912385)
        .setScale(5, BigDecimal.ROUND_HALF_UP)
        .toString();

Berikut adalah saran perpustakaan apa yang dapat Anda gunakan jika Anda menginginkannya double. Saya tidak akan merekomendasikan ini untuk konversi string, karena double mungkin tidak dapat mewakili apa yang Anda inginkan (lihat misalnya di sini ):

  1. Presisi dari Apache Commons Math

    double rounded = Precision.round(0.912385, 5, BigDecimal.ROUND_HALF_UP);
  2. Fungsi dari Colt

    double rounded = Functions.round(0.00001).apply(0.912385)
  3. Utils dari Weka

    double rounded = Utils.roundDouble(0.912385, 5)

18

Anda dapat menggunakan metode utilitas berikut-

public static double round(double valueToRound, int numberOfDecimalPlaces)
{
    double multipicationFactor = Math.pow(10, numberOfDecimalPlaces);
    double interestedInZeroDPs = valueToRound * multipicationFactor;
    return Math.round(interestedInZeroDPs) / multipicationFactor;
}

@mariolpantunes: Ini akan gagal. Coba ini: round(1.005,2);atauround(0.50594724957626620092, 20);
Matthias Braun

Berhasil. Tapi mengambang dan ganda secara tidak resmi adalah perkiraan. Mari kita perhatikan contoh pertama Anda. Jika Anda mencetak output InZeroDPs yang diminati sebelum Math.round, ia akan mencetak 100.49999999999999. Anda kehilangan ketepatan seperti Math.round bulat itu sebagai 100. Karena sifat atau mengapung dan ganda ada kasus garis batas ketika tidak berfungsi dengan baik (informasi lebih lanjut di sini en.wikipedia.org/wiki/Floating_point#Accuracy_problems )
mariolpantunes

ganda itu cepat! desimal lambat. komputer tidak repot memproses pemikiran mereka dalam notasi desimal. Anda harus memberikan beberapa presisi desimal untuk menjaga floating point ganda dengan cepat.
hamish

@hamish Pertanyaannya adalah tentang presisi, bukan tentang kecepatan.
Marquis of Lorne



7

Coba ini: org.apache.commons.math3.util.Precision.round (double x, skala int)

Lihat: http://commons.apache.org/proper/commons-math/apidocs/org/apache/commons/math3/util/Precision.html

Beranda Apache Commons Mathematics Library adalah: http://commons.apache.org/proper/commons-math/index.html

Penerapan internal metode ini adalah:

public static double round(double x, int scale) {
    return round(x, scale, BigDecimal.ROUND_HALF_UP);
}

public static double round(double x, int scale, int roundingMethod) {
    try {
        return (new BigDecimal
               (Double.toString(x))
               .setScale(scale, roundingMethod))
               .doubleValue();
    } catch (NumberFormatException ex) {
        if (Double.isInfinite(x)) {
            return x;
        } else {
            return Double.NaN;
        }
    }
}

7

Karena saya tidak menemukan jawaban lengkap tentang tema ini, saya telah mengumpulkan sebuah kelas yang harus menangani ini dengan benar, dengan dukungan untuk:

  • Memformat : Dengan mudah memformat dobel ke string dengan sejumlah tempat desimal
  • Parsing : Parsing nilai yang diformat kembali menjadi dua kali lipat
  • Lokal : Memformat dan menguraikan menggunakan lokal default
  • Notasi eksponensial : Mulai menggunakan notasi eksponensial setelah batas tertentu

Penggunaannya cukup sederhana :

(Demi contoh ini saya menggunakan lokal kustom)

public static final int DECIMAL_PLACES = 2;

NumberFormatter formatter = new NumberFormatter(DECIMAL_PLACES);

String value = formatter.format(9.319); // "9,32"
String value2 = formatter.format(0.0000005); // "5,00E-7"
String value3 = formatter.format(1324134123); // "1,32E9"

double parsedValue1 = formatter.parse("0,4E-2", 0); // 0.004
double parsedValue2 = formatter.parse("0,002", 0); // 0.002
double parsedValue3 = formatter.parse("3423,12345", 0); // 3423.12345

Ini kelasnya :

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

public class NumberFormatter {

    private static final String SYMBOL_INFINITE           = "\u221e";
    private static final char   SYMBOL_MINUS              = '-';
    private static final char   SYMBOL_ZERO               = '0';
    private static final int    DECIMAL_LEADING_GROUPS    = 10;
    private static final int    EXPONENTIAL_INT_THRESHOLD = 1000000000; // After this value switch to exponential notation
    private static final double EXPONENTIAL_DEC_THRESHOLD = 0.0001; // Below this value switch to exponential notation

    private DecimalFormat decimalFormat;
    private DecimalFormat decimalFormatLong;
    private DecimalFormat exponentialFormat;

    private char groupSeparator;

    public NumberFormatter(int decimalPlaces) {
        configureDecimalPlaces(decimalPlaces);
    }

    public void configureDecimalPlaces(int decimalPlaces) {
        if (decimalPlaces <= 0) {
            throw new IllegalArgumentException("Invalid decimal places");
        }

        DecimalFormatSymbols separators = new DecimalFormatSymbols(Locale.getDefault());
        separators.setMinusSign(SYMBOL_MINUS);
        separators.setZeroDigit(SYMBOL_ZERO);

        groupSeparator = separators.getGroupingSeparator();

        StringBuilder decimal = new StringBuilder();
        StringBuilder exponential = new StringBuilder("0.");

        for (int i = 0; i < DECIMAL_LEADING_GROUPS; i++) {
            decimal.append("###").append(i == DECIMAL_LEADING_GROUPS - 1 ? "." : ",");
        }

        for (int i = 0; i < decimalPlaces; i++) {
            decimal.append("#");
            exponential.append("0");
        }

        exponential.append("E0");

        decimalFormat = new DecimalFormat(decimal.toString(), separators);
        decimalFormatLong = new DecimalFormat(decimal.append("####").toString(), separators);
        exponentialFormat = new DecimalFormat(exponential.toString(), separators);

        decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
        decimalFormatLong.setRoundingMode(RoundingMode.HALF_UP);
        exponentialFormat.setRoundingMode(RoundingMode.HALF_UP);
    }

    public String format(double value) {
        String result;
        if (Double.isNaN(value)) {
            result = "";
        } else if (Double.isInfinite(value)) {
            result = String.valueOf(SYMBOL_INFINITE);
        } else {
            double absValue = Math.abs(value);
            if (absValue >= 1) {
                if (absValue >= EXPONENTIAL_INT_THRESHOLD) {
                    value = Math.floor(value);
                    result = exponentialFormat.format(value);
                } else {
                    result = decimalFormat.format(value);
                }
            } else if (absValue < 1 && absValue > 0) {
                if (absValue >= EXPONENTIAL_DEC_THRESHOLD) {
                    result = decimalFormat.format(value);
                    if (result.equalsIgnoreCase("0")) {
                        result = decimalFormatLong.format(value);
                    }
                } else {
                    result = exponentialFormat.format(value);
                }
            } else {
                result = "0";
            }
        }
        return result;
    }

    public String formatWithoutGroupSeparators(double value) {
        return removeGroupSeparators(format(value));
    }

    public double parse(String value, double defValue) {
        try {
            return decimalFormat.parse(value).doubleValue();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return defValue;
    }

    private String removeGroupSeparators(String number) {
        return number.replace(String.valueOf(groupSeparator), "");
    }

}

7

Jika Anda benar-benar ingin angka desimal untuk perhitungan (dan tidak hanya untuk output), jangan gunakan format floating point berbasis biner seperti ganda.

Use BigDecimal or any other decimal-based format.

Saya menggunakan BigDecimal untuk perhitungan, tapi ingat itu tergantung pada ukuran angka yang Anda hadapi. Dalam sebagian besar implementasi saya, saya menemukan parsing dari double atau integer ke Long sudah cukup untuk perhitungan angka yang sangat besar.

Bahkan, saya baru-baru ini menggunakan parsed-to-Long untuk mendapatkan representasi yang akurat (sebagai lawan hasil hex) dalam GUI untuk angka sebesar ######################### karakter ################ (sebagai contoh).


6

Untuk mencapai ini kita dapat menggunakan formatter ini:

 DecimalFormat df = new DecimalFormat("#.00");
 String resultado = df.format(valor)

atau:

DecimalFormat df = new DecimalFormat("0.00"); :

Gunakan metode ini untuk mendapatkan selalu dua desimal:

   private static String getTwoDecimals(double value){
      DecimalFormat df = new DecimalFormat("0.00"); 
      return df.format(value);
    }

Menentukan nilai-nilai ini:

91.32
5.22
11.5
1.2
2.6

Dengan menggunakan metode ini kita bisa mendapatkan hasil ini:

91.32
5.22
11.50
1.20
2.60

demo online.


5

Hanya dalam kasus seseorang masih membutuhkan bantuan dengan ini. Solusi ini sangat cocok untuk saya.

private String withNoTrailingZeros(final double value, final int nrOfDecimals) {
return new BigDecimal(String.valueOf(value)).setScale(nrOfDecimals,  BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString();

}

mengembalikan a String dengan output yang diinginkan.


Harap sertakan alasan Anda untuk tidak mendukung dalam komentar, jika tidak, itulah yang kami sebut intimidasi.
Asher. M. O

4

Saya setuju dengan jawaban yang dipilih untuk digunakan DecimalFormat--- atau sebagai alternatifBigDecimal .

Silakan baca Pembaruan di bawah ini terlebih dahulu!

Namun jika Anda tidak ingin membulatkan nilai ganda dan mendapatkan doublehasil nilai, Anda dapat menggunakan org.apache.commons.math3.util.Precision.round(..)seperti yang disebutkan di atas. Implementasinya menggunakan BigDecimal, lambat dan membuat sampah.

Metode serupa tetapi cepat dan bebas-sampah disediakan oleh DoubleRounderutilitas di perpustakaan decimal4j:

 double a = DoubleRounder.round(2.0/3.0, 3);
 double b = DoubleRounder.round(2.0/3.0, 3, RoundingMode.DOWN);
 double c = DoubleRounder.round(1000.0d, 17);
 double d = DoubleRounder.round(90080070060.1d, 9);
 System.out.println(a);
 System.out.println(b);
 System.out.println(c);
 System.out.println(d);

Akan menghasilkan

 0.667
 0.666
 1000.0
 9.00800700601E10

Lihat https://github.com/tools4j/decimal4j/wiki/DoubleRounder-Utility

Penafian: Saya terlibat dalam proyek desimal4j.

Pembaruan: Seperti yang ditunjukkan @iaforek, DoubleRounder terkadang mengembalikan hasil yang berlawanan dengan intuisi. Alasannya adalah bahwa ia melakukan pembulatan yang benar secara matematis. ContohnyaDoubleRounder.round(256.025d, 2) akan dibulatkan ke 256,02 karena nilai ganda yang direpresentasikan sebagai 256.025d agak lebih kecil dari nilai rasional 256.025 dan karenanya akan dibulatkan ke bawah.

Catatan:

  • Perilaku ini sangat mirip dengan BigDecimal(double)konstruktor (tetapi tidak valueOf(double)yang menggunakan konstruktor string).
  • Masalahnya dapat diatasi dengan langkah pembulatan ganda ke presisi yang lebih tinggi terlebih dahulu, tetapi rumit dan saya tidak akan membahas detailnya di sini

Untuk alasan itu dan semua yang disebutkan di atas dalam posting ini saya tidak bisa merekomendasikan untuk menggunakan DoubleRounder .


Apakah Anda memiliki metrik yang menunjukkan seberapa efisien solusi Anda dibandingkan dengan yang lain?
iaforek

Saya belum membandingkannya dengan solusi lain tetapi ada tolok ukur jmh yang tersedia dalam kode sumber: github.com/tools4j/decimal4j/blob/master/src/jmh/java/org/… Saya menjalankan patokan pada VM , hasilnya tersedia sebagai file csv di sini: github.com/tools4j/decimal4j/wiki/Performance
marco

1
DoubleRounder gagal untuk kasus-kasus berikut: DoubleRounder.round (256.025d, 2) - diharapkan: 256.03, aktual: 256.02 atau untuk DoubleRounder.round (260.775d, 2) - diharapkan: 260.78, aktual: 260.77.
iaforek

@iaforek: ini benar, karena DoubleRounder melakukan pembulatan matematis yang benar. Namun saya mengakui bahwa ini agak berlawanan dengan intuisi dan karenanya akan memperbarui jawaban saya.
marco

3

Cuplikan kode di bawah ini menunjukkan cara menampilkan n digit. Caranya adalah dengan mengatur variabel pp ke 1 diikuti oleh n nol. Dalam contoh di bawah ini, nilai pp variabel memiliki 5 nol, sehingga 5 digit akan ditampilkan.

double pp = 10000;

double myVal = 22.268699999999967;
String needVal = "22.2687";

double i = (5.0/pp);

String format = "%10.4f";
String getVal = String.format(format,(Math.round((myVal +i)*pp)/pp)-i).trim();

3

Jika Anda menggunakan DecimalFormatuntuk mengkonversi doubleke String, itu sangat mudah:

DecimalFormat formatter = new DecimalFormat("0.0##");
formatter.setRoundingMode(RoundingMode.HALF_UP);

double num = 1.234567;
return formatter.format(num);

Ada beberapa RoundingModenilai enum untuk dipilih, tergantung pada perilaku yang Anda butuhkan.


3

Saya datang ke sini hanya ingin jawaban sederhana tentang cara membulatkan angka. Ini adalah jawaban tambahan untuk memberikan itu.

Cara membulatkan angka di Jawa

Kasus yang paling umum adalah menggunakan Math.round().

Math.round(3.7) // 4

Bilangan dibulatkan ke bilangan bulat terdekat. Sebuah .5nilai dibulatkan. Jika Anda membutuhkan perilaku pembulatan yang berbeda dari itu, Anda bisa menggunakan salah satu fungsi Matematika lainnya . Lihat perbandingan di bawah ini.

bulat

Sebagaimana dinyatakan di atas, ini membulatkan ke bilangan bulat terdekat. .5desimal ditangkap. Metode ini mengembalikan sebuah int.

Math.round(3.0); // 3
Math.round(3.1); // 3
Math.round(3.5); // 4
Math.round(3.9); // 4

Math.round(-3.0); // -3
Math.round(-3.1); // -3
Math.round(-3.5); // -3 *** careful here ***
Math.round(-3.9); // -4

langit-langit

Nilai desimal apa pun dibulatkan ke bilangan bulat berikutnya. Pergi ke langit - langit . Metode ini mengembalikan a double.

Math.ceil(3.0); // 3.0
Math.ceil(3.1); // 4.0
Math.ceil(3.5); // 4.0
Math.ceil(3.9); // 4.0

Math.ceil(-3.0); // -3.0
Math.ceil(-3.1); // -3.0
Math.ceil(-3.5); // -3.0
Math.ceil(-3.9); // -3.0

lantai

Nilai desimal apa pun dibulatkan ke bilangan bulat berikutnya. Metode ini mengembalikan a double.

Math.floor(3.0); // 3.0
Math.floor(3.1); // 3.0
Math.floor(3.5); // 3.0
Math.floor(3.9); // 3.0

Math.floor(-3.0); // -3.0
Math.floor(-3.1); // -4.0
Math.floor(-3.5); // -4.0
Math.floor(-3.9); // -4.0

rintis

Ini mirip dengan putaran dalam nilai desimal itu yang bulat ke bilangan bulat terdekat. Namun, tidak seperti round, .5nilai membulatkan ke bilangan bulat genap. Metode ini mengembalikan a double.

Math.rint(3.0); // 3.0
Math.rint(3.1); // 3.0
Math.rint(3.5); // 4.0 ***
Math.rint(3.9); // 4.0
Math.rint(4.5); // 4.0 ***
Math.rint(5.5); // 6.0 ***

Math.rint(-3.0); // -3.0
Math.rint(-3.1); // -3.0
Math.rint(-3.5); // -4.0 ***
Math.rint(-3.9); // -4.0
Math.rint(-4.5); // -4.0 ***
Math.rint(-5.5); // -6.0 ***

1
Anda hanya memecahkan kasus pembulatan ke 0 desimal. Pertanyaan aslinya lebih umum.
lukas84

3

Jika Anda menggunakan teknologi yang memiliki JDK minimal. Inilah cara tanpa Java libs:

double scale = 100000;    
double myVal = 0.912385;
double rounded = (int)((myVal * scale) + 0.5d) / scale;

Ini akan gagal dalam kasus di mana myVal tidak kurang dari 1 dan dengan nol setelah desimal di luar nilai skala. Katakanlah Anda memiliki myVal = 9.00000000912385; Di atas akan mengembalikan 9.0. Saya pikir kita harus memberikan solusi yang berfungsi dalam semua kasus myVal. Tidak khusus untuk nilai yang Anda nyatakan.
tavalendo

@ user102859 Dalam contoh Anda, 9.0 adalah hasil yang benar. Saya tidak mengerti bagaimana ini akan gagal.
Craigo

2

DecimalFormat adalah cara terbaik untuk menghasilkan, tapi saya tidak suka itu. Saya selalu melakukan ini sepanjang waktu, karena mengembalikan nilai ganda. Jadi saya bisa menggunakannya lebih dari sekedar output.

Math.round(selfEvaluate*100000d.0)/100000d.0;

ATAU

Math.round(selfEvaluate*100000d.0)*0.00000d1;

Jika Anda membutuhkan nilai tempat desimal besar, Anda dapat menggunakan BigDecimal sebagai gantinya. Bagaimanapun juga .0itu penting. Tanpanya pembulatan 0.33333d5 menghasilkan 0.33333 dan hanya 9 digit yang dibolehkan. Fungsi kedua tanpa .0memiliki masalah dengan 0,30000 mengembalikan 0,30000000000000004.


2

inilah jawaban saya:

double num = 4.898979485566356;
DecimalFormat df = new DecimalFormat("#.##");      
time = Double.valueOf(df.format(num));

System.out.println(num); // 4.89

1

Jadi setelah membaca sebagian besar jawaban, saya menyadari sebagian besar dari mereka tidak akan tepat, pada kenyataannya menggunakan BigDecimalsepertinya pilihan terbaik, tetapi jika Anda tidak mengerti cara RoundingModekerjanya, Anda pasti akan kehilangan presisi. Saya menemukan ini ketika bekerja dengan angka-angka besar dalam sebuah proyek dan berpikir itu bisa membantu orang lain mengalami kesulitan dalam mengumpulkan angka. Sebagai contoh.

BigDecimal bd = new BigDecimal("1363.2749");
bd = bd.setScale(2, RoundingMode.HALF_UP);
System.out.println(bd.doubleValue());

Anda akan mengharapkan untuk mendapatkan 1363.28sebagai output, tetapi Anda akan berakhir dengan 1363.27, yang tidak diharapkan, jika Anda tidak tahu apa yang RoundingModedilakukannya. Jadi melihat ke dalam Oracle Docs , Anda akan menemukan deskripsi berikut untuk RoundingMode.HALF_UP.

Mode pembulatan ke putaran menuju "tetangga terdekat" kecuali jika kedua tetangga sama-sama berjarak, dalam hal ini pembulatan ke atas.

Jadi mengetahui ini, kami menyadari bahwa kami tidak akan mendapatkan pembulatan yang tepat, kecuali jika kami ingin berputar ke arah tetangga terdekat . Jadi, untuk mencapai putaran yang memadai, kita perlu mengulang dari angka n-1desimal ke angka desimal yang diinginkan. Sebagai contoh.

private double round(double value, int places) throws IllegalArgumentException {

    if (places < 0) throw new IllegalArgumentException();

    // Cast the number to a String and then separate the decimals.
    String stringValue = Double.toString(value);
    String decimals = stringValue.split("\\.")[1];

    // Round all the way to the desired number.
    BigDecimal bd = new BigDecimal(stringValue);
    for (int i = decimals.length()-1; i >= places; i--) {
        bd = bd.setScale(i, RoundingMode.HALF_UP);
    }

    return bd.doubleValue();
}

Ini pada akhirnya akan memberi kita hasil yang diharapkan, yang akan menjadi 1363.28.


0

Di mana dp = tempat desimal yang Anda inginkan, dan nilainya dua kali lipat.

    double p = Math.pow(10d, dp);

    double result = Math.round(value * p)/p;

1
Menghasilkan 1.0untuk value = 1.005dan dp = 2. Gunakan ini sebagai gantinya.
Matthias Braun

tidak apa-apa Matt, contoh Anda tidak valid. karena 1,005 tidak dapat diwakili dalam floating point double pula. itu harus disimpan benar-benar di atas atau di bawah 1,005 yaitu disimpan sebagai dua kali lipat ketika Anda mengkompilasi: 1,0049998 (itu tidak disimpan sebagai desimal dalam kode kompilasi Anda karena Anda akan membuat pembaca percaya) tujuannya benar, ia menyimpan nilai sebagai floating point gandakan, di mana kasus pinggiran seperti milikmu tidak signifikan. jika ya, maka Anda akan menggunakan 3dp lalu mengonversinya menjadi desimal, lalu melakukan fungsi putaran desimal, sama seperti tautan yang Anda pasang.
hamish

1
@ Ashish Saya tidak melihat di mana Matthias 'akan membuat pembaca percaya' hal apa pun yang nilainya dikompilasi sebagai desimal. Jangan menaruh kata-kata ke mulut orang lain.
Marquis of Lorne

0

Perlu diingat bahwa String.format () dan DecimalFormat menghasilkan string menggunakan Lokal default. Jadi mereka dapat menulis angka yang diformat dengan titik atau koma sebagai pemisah antara bagian integer dan desimal. Untuk memastikan bahwa String bulat dalam format yang Anda inginkan, gunakan java.text.NumberFormat seperti itu:

  Locale locale = Locale.ENGLISH;
  NumberFormat nf = NumberFormat.getNumberInstance(locale);
  // for trailing zeros:
  nf.setMinimumFractionDigits(2);
  // round to 2 digits:
  nf.setMaximumFractionDigits(2);

  System.out.println(nf.format(.99));
  System.out.println(nf.format(123.567));
  System.out.println(nf.format(123.0));

Akan mencetak dalam bahasa Inggris lokal (tidak peduli apa lokal Anda): 0.99 123.57 123.00

Contoh ini diambil dari Farenda - cara mengkonversi ganda ke String dengan benar .


0

Secara umum, pembulatan dilakukan dengan penskalaan: round(num / p) * p

/**
 * MidpointRounding away from zero ('arithmetic' rounding)
 * Uses a half-epsilon for correction. (This offsets IEEE-754
 * half-to-even rounding that was applied at the edge cases).
 */
double RoundCorrect(double num, int precision) {
    double c = 0.5 * EPSILON * num;
//  double p = Math.pow(10, precision); //slow
    double p = 1; while (precision--> 0) p *= 10;
    if (num < 0)
        p *= -1;
    return Math.round((num + c) * p) / p;
}

// testing edge cases
RoundCorrect(1.005, 2);   // 1.01 correct
RoundCorrect(2.175, 2);   // 2.18 correct
RoundCorrect(5.015, 2);   // 5.02 correct

RoundCorrect(-1.005, 2);  // -1.01 correct
RoundCorrect(-2.175, 2);  // -2.18 correct
RoundCorrect(-5.015, 2);  // -5.02 correct
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.