Java'da bir sayı n ondalık basamağa yuvarlanır


1259

Ne istiyorum yarım-up yöntemi kullanarak yuvarlar bir dize bir çift dönüştürmek için bir yöntemdir - yani yuvarlanacak ondalık 5 ise, her zaman sonraki sayıya yuvarlar. Bu, çoğu durumda çoğu insanın beklediği standart yuvarlama yöntemidir.

Ayrıca sadece önemli basamakların görüntülenmesini istiyorum - yani herhangi bir sıfır yok.

Bunu yapmak için bir String.formatyöntem yöntemi kullanmak olduğunu biliyorum :

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

İadeler:

0.91239

bu harika, ancak anlamlı olmasa bile her zaman 5 ondalık basamaklı sayılar görüntüler:

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

İadeler:

0.91230

Başka bir yöntem DecimalFormatter:

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

İadeler:

0.91238

Ancak gördüğünüz gibi bu yarım çift yuvarlama kullanır. Yani bir önceki rakam çiftse yuvarlanır. Ne istiyorum bu:

0.912385 -> 0.91239
0.912300 -> 0.9123

Java ile bunu başarmanın en iyi yolu nedir?

Yanıtlar:


751

Kullanın setRoundingMode,RoundingMode sorununuzu yarım çift turla açıkça ele şekilde , ardından gerekli çıktınız için biçim desenini kullanın.

Misal:

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

çıktı verir:

12
123.1235
0.23
0.1
2341234.2125

EDIT : Orijinal cevap çift değerlerin doğruluğunu ele almaz. Eğer yukarı ya da aşağı yuvarlama umurumda değil eğer bu iyi. Ancak, doğru yuvarlama yapmak istiyorsanız, değerlerin beklenen doğruluğunu dikkate almanız gerekir. Kayan nokta değerleri dahili olarak bir ikili temsile sahiptir. Bu, 2.7735 gibi bir değerin gerçekte dahili olarak tam değere sahip olmadığı anlamına gelir. Biraz daha büyük veya biraz daha küçük olabilir. Dahili değer biraz daha küçükse, 2.7740'a yuvarlanmaz. Bu durumu düzeltmek için üzerinde çalıştığınız değerlerin doğruluğunun farkında olmanız ve yuvarlamadan önce bu değeri eklemeniz veya çıkarmanız gerekir. Örneğin, değerlerinizin 6 basamağa kadar doğru olduğunu bildiğinizde, yarım değerleri yuvarlamak için bu doğruluğu değere ekleyin:

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

Yuvarlamak için doğruluğu çıkarın.


9
Bu muhtemelen şimdiye kadar sunulan en iyi çözümdür. DecimalFormat sınıfına ilk baktığımda bu tesisi görmememin nedeni, sadece Java 1.6'da tanıtıldı. Ne yazık ki 1.5 kullanmakla sınırlıyım ama gelecek için bilmek faydalı olacaktır.
Alex Spurling

1
Bunu: "#.##"yuvarlama ile denedim HALF_UP. 256.335f-> "256.33"... (örnek yorumlardan @ yıldız yanıtına gelir).
bigstones

6
Lütfen DecimalFormat geçerli Yerel yapılandırmanıza bağlı olduğundan dikkatli olun, ayırıcı olarak nokta alamayabilirsiniz. Şahsen Asterite'nin cevabını aşağıda tercih ediyorum
Gomino

1
Ayrıca, DecimalFormat'ın iş parçacığı açısından güvenli olmasını beklememeniz gerektiğini unutmayın. Gereğince Java docs : Ondalık biçimler genel senkronize edilmez. Her bir iş parçacığı için ayrı format örnekleri oluşturmanız önerilir. Birden çok iş parçacığı bir biçime aynı anda erişiyorsa, dış olarak eşitlenmelidir.
CGK

1
nasıl düzgün bir yuvarlama yapar, böylece 0.0004 ila 0.001 yuvarlamayacak nasıl yapılır

471

Varsayarsak valuebir olduğunu double, bunu yapabilirsiniz:

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

Bu 5 basamak hassasiyeti içindir. Sıfır sayısı, ondalıkların sayısını gösterir.


71
GÜNCELLEME: Bunu yapmanın DecimalFormat kullanmaktan daha hızlı olduğunu onayladım. 200 kez DecimalFormat kullanarak döngü ve bu yöntem. DecimalFormat 200 döngüyü tamamlamak için 14ms aldı, bu yöntem 1ms'den az sürdü. Şüphelendiğim gibi, bu daha hızlı. Saat döngüsüyle ödeme alırsanız, yapmanız gereken budur. Chris Cudmore'un dürüst olmak için söylediklerini bile söylemesine şaşırdım. nesneleri ayırmak her zaman ilkelleri dökmekten ve decimalFormat.format () yerine statik yöntemler (Math.round () kullanmaktan daha pahalıdır.
Andi Jay

99
Bu teknik vakaların% 90'ından fazlasında başarısız olur. -1.
user207421

25
Nitekim, bu başarısız: Math.round(0.1 * Math.pow(10,20))/Math.pow(10,20) == 0.09223372036854775.
Robert Tupelo-Schneck

54
Bu yöntemi (veya kayan noktaların yuvarlanmasını) kullanırken çok dikkatli olun. 265.335 kadar basit bir şey için başarısız olur. 265.335 * 100 (2 basamak hassasiyeti) ara sonucu 26533.499999999996'dır. Bu 265.33'e yuvarlandığı anlamına gelir. Kayan nokta sayılarından gerçek ondalık sayılara dönüştürürken doğal sorunlar vardır. EJP'nin cevabını buradan stackoverflow.com/a/12684082/144578
Sebastiaan van den Broek

6
@SebastiaanvandenBroek: Vay be yanlış bir cevap almanın bu kadar kolay olduğunu hiç bilmiyordum. Ancak, kesin olmayan sayılarla çalışıyorsa, herhangi bir değerin kesin olmadığını kabul etmelidir . 265.335gerçekten 265.335 += tolerance, toleransın önceki işlemlere ve girdi değerleri aralığına bağlı olduğu anlamına gelir . Doğru, kesin değeri bilmiyoruz. Edge değerlerinde, her iki yanıt da tartışmasız doğrudur. Kesin olmamız gerekirse, iki katına çıkmamalıyız. failBurada çift için geri dönüştürme değildir. Onun OP düşünme 265.335tam olarak bu olarak gelen güvenebilirsiniz .
ToolmakerSteve

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

size bir BigDecimal. Bunun dışında dize almak üzere yalnızca bu çağrı BigDecimal'ın toStringyöntem veyatoPlainString düz bir biçim dizesi için Java için bir yöntem 5+.

Örnek program:

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
Bu benim tercih ettiğim çözüm. Daha da kısa: BigDecimal.valueOf (doubleVar) .setScale (boyutunuz burada, BigDecimal.ROUND_HALF_UP); BigDecimal.valueOf (çift val) aslında başlık altında Double.toString () 'i çağırır;)
Etienne Neveu

4
Güzel. Köşeleri kesmeyin ve new BigDecimal(doubleVar)kayan noktaların yuvarlanmasıyla ilgili sorunlarla karşılaşabileceğiniz için kullanmayın
Edd

8
@Eddd, ilginç bir şekilde, SebastiaanvandenBroek'in yıldız işaretinin cevabına yorumda bahsedilmesi durumunda yuvarlama sorunu ortaya çıkıyor. double val = 265.335;, BigDecimal.valueOf(val).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString();=> 265.34, ancak (new BigDecimal(val)).setScale(decimals, BigDecimal.ROUND_HALF_UP).toPlainString();=> 265.33.
ToolmakerSteve

7
@ToolmakerSteve Bunun nedeni, çiftle yeni BigDecimal kullanmanın çift değeri doğrudan alması ve BigDecimal'i oluşturmak için bunu kullanmaya çalışmasıdır, oysa BigDecimal.valueOf veya tostring formunu dönüştürme işleminden önce bir dizeye (daha kesin bir gösterim) ayrıştırır .
MetroidFan2002

116

Ayrıca

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

takip eden 0'lara sahip olduğunuzdan emin olmak için.


25
Ben sorunun hedeflerinden biri olduğunu "Orada gerektiğini olduğuna inanmak değil sonda sıfır olması".
Lunchbox

7
Bu soru için, op sıfır istemiyordu, ama tam da istediğim buydu. 3 ondalık basamaklı bir sayı listeniz varsa, 0 olsa bile hepsinin aynı rakamlara sahip olmasını istersiniz.
Tom Kincaid

Belirtmeyi unuttunRoundingMode.
IgorGanapolsky

1
@IgorGanapolsky varsayılan olarak Decimal modekullanırRoundingMode.HALF_EVEN.
EndermanAPM

87

Diğerlerinin de belirttiği gibi, doğru cevap ya DecimalFormatya kullanmaktır BigDecimal. Kayan nokta değil sahip ondalık basamak değil muhtemelen yuvarlak can böylece / ilk etapta bunlardan belirli bir sayıya kesiğinin. Ondalık bir yarıçapta çalışmalısınız ve bu iki sınıfın yaptığı şey budur.

Ben bu iş parçacığında ve gerçekten de tüm StackOverflow (ve başka bir yerde) çarpma ve ardından bölme ardından kesilmesi tavsiye tüm cevaplar için bir sayaç örneği olarak aşağıdaki kodu gönderiyorum. Bu tekniğin savunucuları, aşağıdaki kodun vakaların% 92'sinden fazlasında neden yanlış çıktı ürettiğini açıklamakla görevlidir.

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

Bu programın çıktısı:

10001 trials 9251 errors

DÜZENLEME: Ben kullanarak test döngü modülü bölümünü redid aşağıda bazı yorumlar ele almak BigDecimalve new MathContext(16)aşağıdaki gibi modülü operasyon için:

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

Sonuç:

10001 trials 4401 errors

8
İşin püf noktası, tüm 9251 hatalarınızda yazdırılan sonucun hala doğru olmasıdır.
Didier L

7
@DidierL Beni şaşırtmıyor. İlk sayısal dersim olarak 'Sayısal Yöntemler' yapma ve en başta kayan noktanın neler yapabileceği ve yapamayacağı konusunda çok iyi bir şansım vardı. Çoğu programcı bu konuda oldukça belirsizdir.
user207421

15
Yaptığın tek şey, yüzmenin pek çok ondalık değeri tam olarak temsil etmediğini reddetmek. Yuvarlama bir soruna neden olmaz. Kabul ettiğiniz gibi, sayılar yine de beklendiği gibi yazdırılır.
Peter Lawrey

8
Testiniz bozuldu, yuvarlayın () ve test zamanın% 94'ünde başarısız oluyor. ideone.com/1y62CY baskılar Başarılı100 trials 94 errors bir testle başlamalı ve yuvarlamanın getirilmesinin testi bozduğunu göstermelisiniz.
Peter Lawrey

6
Çürütme, burada çürütüldü. Math.round'un bu aralıkta doublehatasız olarak kullanılması ideone.com/BVCHh3
Peter Lawrey

83

Varsayalım

double d = 9232.129394d;

kullanabilirsiniz BigDecimal

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

veya BigDecimal olmadan

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

her iki çözümle d == 9232.13


2
Bence bu Java 1.5 kullanıcıları (ve aşağısı) için en iyi çözümdür. Bir yorum tho, HALF_EVEN yuvarlama modunu kullanmayın, çünkü tek ve çift sayılar için farklı davranışlar (2.5 mermi 2'ye, 5.5 mermi 6'ya mesela), eğer istediğiniz bu değilse.
IcedDante

4
İlk çözüm doğru: ikincisi çalışmıyor. Kanıt için buraya bakın .
19:13

1
@EJP: İlk çözüm bile RoundingMode.HALF_UPyanlış. İle deneyin 1.505. Doğru yol kullanmaktır BigDecimal.valueOf(d).
Matthias Braun

Matthias Braun, çözüm iyi, bu yüzden 31 ups .. 1.505 almak ve çiftten ondalık dönüştürmek istiyorsanız, o zaman önce Double.toString (x) dönüştürmek için 1.505 ondalık kayan nokta 1.50499998 olarak saklanır daha sonra bir BigDecimal () içine koyun, ancak bu son derece yavaştır ve ilk etapta hız için çift kullanma amacını yener.
hamish

1
BigDecimal (225 ms) ve Math.round (2 ms) yolu ile 100k bir döngü koştu ve işte zamanlama ... Alınan Süre: 225 mili saniye kullanarak dönüştürmek için: 9232.13 Alınan Süre: dönüştürmek için 2 mili saniye : 9232.13 techiesinfo.com
user1114134

59

DecimalFormat sınıfını kullanabilirsiniz.

double d = 3.76628729;

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

Neden Double.valueOf()seçilmesinin bir nedeni var Double.parseDouble()? valueOf()Yöntem, bir döner Doubleiken, nesne parseDouble(), bir döner doubleilkel. Geçerli kodun yazılma şekliyle, twoDoubledeğişkeninizin beklediği ilkeye, fazladan bir bayt kodu işlemine döndürmek için dönüşe otomatik kutulamayı da uygularsınız . Bunun parseDouble()yerine kullanacağım cevabı değiştirirdim .
ecbrodie

Double.parseDouble()Stringgirdi gerekiyor .
Ryde

38

Real Java nasıl yapılır mesajların da Java 1.6 önceki versiyonlar için uyumlu olan bu çözüm.

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
evet bu math.round'un pozitif sayılar için yaptığı şeydir, fakat bunu negatif sayılarla denediniz mi? insanlar negatif sayıları da ele almak için diğer çözümlerde math.round kullanıyor.
hamish

Not: Math.floor(x + 0.5)veMath.round(x)
Peter Lawrey

30

@Milhous: yuvarlama için ondalık biçim mükemmel:

Ayrıca

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

takip eden 0'lara sahip olduğunuzdan emin olmak için.

Bu yöntemin gerçek bir sayısal, yuvarlama mekanizması sağlamada çok iyi olduğunu ekleyeceğim - sadece görsel olarak değil, aynı zamanda işlerken.

Varsayımsal: GUI programına yuvarlama mekanizması uygulamanız gerekir. Bir sonuç çıktısının doğruluğunu / hassasiyetini değiştirmek için yalnızca düzeltme biçimi biçimini değiştirin (yani köşeli parantezler içinde). Böylece:

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

çıktı olarak dönecekti: 0.912385

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

çıktı olarak dönecekti: 0.91239

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

çıktı olarak dönecekti: 0.9124

[DÜZENLEME: ayrıca düzeltme biçimi biçimi böyleyse ("# 0. ############") ve bağımsız değişken uğruna ondalık, örneğin 3.1415926 girerseniz, DecimalFormat herhangi bir çöp üretmez ( örneğin sıfırlar) ve döndürür: 3.1415926.. eğer bu şekilde eğimliyseniz. Verilmiş, bazı geliştiricilerin beğenisi için biraz ayrıntılı - ama hey, işleme sırasında düşük bir bellek ayak izine sahip ve uygulanması çok kolay.]

Aslında, DecimalFormat'ın güzelliği, dize görünümünü ve aynı zamanda yuvarlama hassasiyeti seviyesini aynı anda ele almasıdır. Ergo: Bir kod uygulamasının fiyatı için iki avantaj elde edersiniz. ;)


2
Hesaplama için gerçekten ondalık sayılar istiyorsanız (ve yalnızca çıktı için değil), ikili tabanlı kayan nokta biçimini kullanmayın gibi double. BigDecimal veya ondalık tabanlı başka bir biçim kullanın.
Paŭlo Ebermann

20

Sonucu Dize olarak istiyorsanız, kullanabileceğiniz şeylerin bir özeti aşağıda verilmiştir:

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

doubleSonuç olarak isterseniz hangi kitaplıkları kullanabileceğinize dair bir öneri . Ben dize dönüşüm için tavsiye etmem, ancak, çift tam olarak ne istediğinizi temsil edemeyebilir (örneğin buraya bakın ):

  1. Apache Commons Math'dan hassasiyet

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

    double rounded = Functions.round(0.00001).apply(0.912385)
  3. Utils ___ ' dan Weka

    double rounded = Utils.roundDouble(0.912385, 5)

18

Aşağıdaki yardımcı program yöntemini kullanabilirsiniz.

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

@mariolpantunes: Başarısız olacak. round(1.005,2);round(0.50594724957626620092, 20);
Matthias Braun

İşe yarıyor. Ancak, bilgisiz olarak yüzer ve çiftler yaklaşık değerlerdir. İlk örneğinizi ele alalım. InterestingInZeroDP'lerin çıktısını Math.round'dan yazdırırsanız 100.49999999999999 yazdıracaktır. Math.round gibi 100 gibi hassasiyeti kaybettiniz. Doğa veya şamandıralar ve çiftler nedeniyle düzgün çalışmadığı sınır çizgileri vakaları var (daha fazla bilgi burada en.wikipedia.org/wiki/Floating_point#Accuracy_problems )
mariolpantunes

çift ​​hızlı! ondalık yavaş. bilgisayarlar düşüncelerini ondalık gösterimde işlemekle uğraşmazlar. kayan noktayı iki kat hızlı tutmak için ondalık hassasiyetten vazgeçmelisiniz.
hamish

@hamish Soru hızla ilgili değil hassasiyetle ilgilidir.
user207421



7

Şunu deneyin: org.apache.commons.math3.util.Precision.round (çift x, int ölçeği)

Bkz. Http://commons.apache.org/proper/commons-math/apidocs/org/apache/commons/math3/util/Precision.html

Apache Commons Matematik Kütüphanesi ana sayfası: http://commons.apache.org/proper/commons-math/index.html

Bu yöntemin dahili uygulaması:

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

Bu temayla ilgili tam bir cevap bulamadığım için, bunu doğru bir şekilde ele alması gereken bir sınıfı bir araya getirdim:

  • Biçimlendirme : Bir dizeyi belirli sayıda ondalık basamakla dizeye kolayca biçimlendirin
  • Ayrıştırma : Biçimlendirilmiş değeri ikiye böler
  • Yerel ayar: Varsayılan yerel ayarı kullanarak biçimlendirme ve ayrıştırma
  • Üstel gösterim : Belirli bir eşikten sonra üstel gösterim kullanmaya başlayın

Kullanımı oldukça basit :

(Bu örnek uğruna özel bir yerel ayar kullanıyorum)

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

İşte sınıf :

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

Hesaplama için gerçekten ondalık sayılar istiyorsanız (ve yalnızca çıktı için değil), ikili gibi ikili tabanlı kayan nokta biçimini kullanmayın.

Use BigDecimal or any other decimal-based format.

Hesaplamalar için BigDecimal kullanıyorum, ancak uğraştığınız sayıların boyutuna bağlı olduğunu unutmayın. Uygulamalarımın çoğunda, çift veya tamsayıdan Long'a ayrışmanın çok büyük sayı hesaplamaları için yeterli olduğunu düşünüyorum.

Aslında, kısa süre önce ################### kadar büyük sayılar için bir GUI'de doğru temsiller almak için (onaltılık sonuçların aksine) kullandım ############### karakterler (örnek olarak).


6

Bunu başarmak için şu formatlayıcıyı kullanabiliriz:

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

veya:

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

Her zaman iki ondalık sayı almak için bu yöntemi kullanın:

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

Bu değerleri tanımlama:

91.32
5.22
11.5
1.2
2.6

Yöntemi kullanarak bu sonuçları elde edebiliriz:

91.32
5.22
11.50
1.20
2.60

demo çevrimiçi.


5

Birisinin hala bu konuda yardıma ihtiyacı olması durumunda. Bu çözüm benim için mükemmel çalışıyor.

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

}

String istenen çıktıya sahip bir a döndürür .


Lütfen aşağıya oy verme nedeninizi yorumda belirtin, aksi takdirde yıldırma diyoruz.
Asher. M. O

4

DecimalFormat--- veya alternatif olarak kullanmak için seçilen cevaba katılıyorum BigDecimal.

Lütfen önce aşağıdaki Güncellemeyi okuyun !

Eğer Ancak do çift değerini yuvarlamak ve almak istiyorum doubledeğer sonucunu kullanabilirsiniz org.apache.commons.math3.util.Precision.round(..)yukarıda da belirtildiği gibi. Uygulama BigDecimalyavaş kullanır ve çöp oluşturur.

Benzer ama hızlı ve çöp içermeyen bir yöntem DoubleRounderondalık4j kütüphanesindeki yardımcı program tarafından sağlanır :

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

Çıktı olacak

 0.667
 0.666
 1000.0
 9.00800700601E10

Bkz. Https://github.com/tools4j/decimal4j/wiki/DoubleRounder-Utility

Feragatname: Ondalık4j projesinde yer alıyorum.

Güncelleme: @iaforek'in işaret ettiği gibi DoubleRounder bazen mantıksız sonuçlar döndürür. Bunun nedeni, matematiksel olarak doğru yuvarlama yapmasıdır. Örneğin DoubleRounder.round(256.025d, 2), 256.025d olarak temsil edilen çift değer rasyonel değer 256.025'ten biraz daha küçük olduğundan aşağı yuvarlanacağı için 256.02'ye yuvarlanacaktır.

Notlar:

  • Bu davranış yapıcıya çok benzer BigDecimal(double)(ancak valueOf(double)dize yapıcısını kullananlara değil ).
  • Sorun önce daha yüksek bir hassasiyete çift yuvarlama adımıyla atlatılabilir, ancak karmaşıktır ve burada ayrıntılara girmiyorum

Bu nedenlerle ve bu yazıda yukarıda belirtilen her şey için DoubleRounder'ı kullanmanızı tavsiye edemem .


Çözümünüzün diğer çözümlerle karşılaştırıldığında ne kadar verimli olduğunu gösteren metrikleriniz var mı?
iaforek

Diğer çözümlerle karşılaştırmadım, ancak kaynak kodda bir jmh ölçütü var: github.com/tools4j/decimal4j/blob/master/src/jmh/java/org/… Kıyaslama bir VM üzerinde çalıştırdım , sonuçlar csv dosyası olarak buradan edinilebilir
marco

1
DoubleRounder aşağıdaki durumlar için başarısız olur: DoubleRounder.round (256.025d, 2) - beklenen: 256.03, gerçek: 256.02 veya DoubleRounder.round (260.775d, 2) için - beklenen: 260.78, gerçek: 260.77.
iaforek

@iaforek: bu doğrudur, çünkü DoubleRounder matematiksel olarak doğru yuvarlama yapar. Ancak bunun biraz mantıksız olduğunu ve dolayısıyla cevabımı buna göre güncelleyeceğini itiraf ediyorum.
marco

3

Aşağıdaki kod snippet'i n basamağın nasıl görüntüleneceğini gösterir. Hüner, pp değişkenini 1 ve ardından n sıfır olarak ayarlamaktır. Aşağıdaki örnekte, değişken pp değeri 5 sıfır olduğundan 5 basamak görüntülenir.

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

DecimalFormatDönüştürmek doubleiçin kullanıyorsanız String, çok basittir:

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

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

RoundingModeİstediğiniz davranışa bağlı olarak, aralarından seçim yapabileceğiniz çeşitli numaralandırma değerleri vardır .


3

Buraya sadece bir sayının nasıl yuvarlanacağına dair basit bir cevap vermek istedim. Bu, bunu sağlamak için ek bir cevaptır.

Java'da bir sayı nasıl yuvarlanır

En yaygın durum kullanmaktır Math.round().

Math.round(3.7) // 4

Sayılar en yakın tam sayıya yuvarlanır. Bir .5değer yuvarlanır. Bundan farklı bir yuvarlama davranışına ihtiyacınız varsa, diğer Math'lardan birini kullanabilirsiniz. işlevlerinden . Aşağıdaki karşılaştırmaya bakın.

yuvarlak

Yukarıda belirtildiği gibi, bu en yakın tam sayıya yuvarlar. .5ondalık sayılar yuvarlanır. Bu yöntem bir 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

tavan yapmak

Herhangi bir ondalık değer bir sonraki tam sayıya yuvarlanır. Tavana gider . Bu yöntem a döndürür 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

zemin

Herhangi bir ondalık değer bir sonraki tam sayıya yuvarlanır. Bu yöntem a döndürür 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

rint

Bu, ondalık değerlerin en yakın tam sayıya yuvarlanmasında yuvarlamaya benzer. Ancak aksine round, .5değerler çift tam yuvarlak. Bu yöntem a döndürür 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
yalnızca 0 ondalık sayıya yuvarlama durumunu çözüyorsunuz. Orijinal soru daha geneldir.
lukas84

3

Minimum JDK değerine sahip bir teknoloji kullanıyorsanız. Java kütüphanesi olmayan bir yol:

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

Bu, myVal değerinin 1'den küçük olmadığı ve ondalık değerden sonra ölçek değerinin ötesinde sıfırlar olduğu durumlarda başarısız olur. MyVal = 9.00000000912385'iniz olduğunu varsayalım; Yukarıdaki 9.0 dönecektir. Tüm myVal vakalarında çalışan bir çözüm sunmamız gerektiğini düşünüyorum. Özellikle belirttiğiniz değer için değil.
tavalendo

@ user102859 Örneğin, 9.0 doğru sonuçtur. Bunun nasıl başarısız olacağını anlamıyorum.
Craigo

2

DecimalFormat çıktı almak için en iyi yol, ama ben tercih etmiyorum. Bunu her zaman yaparım, çünkü çift değer döndürür. Bu yüzden sadece çıktıdan daha fazlasını kullanabilirim.

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

VEYA

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

Büyük ondalık basamak değerine ihtiyacınız varsa, bunun yerine BigDecimal kullanabilirsiniz. Her neyse .0önemlidir. Bu olmadan, 0.33333d5'in yuvarlanması 0.33333'e geri döner ve sadece 9 hane izin verir. İkinci fonksiyonun .00.30000 dönüş 0.30000000000000004 ile ilgili problemleri vardır.


2

İşte cevabım:

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

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

1

Bu yüzden cevapların çoğunu okuduktan sonra, çoğunun kesin olmayacağını fark ettim, aslında kullanmak BigDecimalen iyi seçim gibi görünüyor, ancak RoundingModeçalışmaların nasıl olduğunu anlamadıysanız , kaçınılmaz olarak hassasiyeti kaybedeceksiniz. Bir projede büyük sayılarla çalışırken bunu anladım ve başkalarının sayıları yuvarlamakta zorluk çekmesine yardımcı olabileceğini düşündüm. Örneğin.

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

1363.28Bir çıktı olarak almayı beklersiniz , ancak 1363.27bunun ne RoundingModeyaptığını bilmiyorsanız, beklenmeyen ile sonuçlanacaksınız . Oracle Docs'u incelediğinizde , aşağıdaki açıklamayı bulacaksınız RoundingMode.HALF_UP.

Her iki komşu da eşit uzaklıkta olmadığı sürece, yuvarlama modu "en yakın komşu" ya doğru yuvarlanır, bu durumda yuvarlanır.

Bunu bilerek, en yakın komşuya yönelmek istemedikçe tam bir yuvarlanma olmayacağımızı fark ettik . Bu nedenle, yeterli bir tur elde etmek için n-1, ondalık basamaktan istenen ondalık basamağa doğru döngü yapmamız gerekir . Örneğin.

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

Bu da bize beklenen çıktıyı verecektir 1363.28.


0

Burada dp = istediğiniz ondalık basamak ve değer iki katıdır.

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

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

1
Üretir 1.0için value = 1.005ve dp = 2. Kullanın bu yerine.
Matthias Braun

Sorun değil Matt, örneğiniz geçerli değil. çünkü 1.005 yine de kayan nokta çiftinde temsil edilemez. gerçekten 1.005'in üzerinde veya altında saklanması gerekir, yani derlediğinizde çift olarak saklanır: 1.0049998 (okuyucuların inandığı gibi derlenmiş kodunuzda ondalık olarak saklanmaz) amaç doğrudur, değerleri kayan nokta olarak saklar sizinki gibi saçak davaları zaten önemsiz. öyleyse, 3dp'yi kullanırsınız, daha sonra ondalık sayıya dönüştürürsünüz, daha sonra yayınladığınız bağlantı gibi ondalık bir yuvarlak işlev yaparsınız.
hamish

1
@hamish Matthias'ın okuyucuların nerede değer ondalık olarak derlendiği gibi herhangi bir şeye inandığını görmüyorum. Başkalarının ağzına kelimeler koymayın.
user207421

0

String.format () ve DecimalFormat öğelerinin varsayılan Yerel Ayar'ı kullanarak dize oluşturduğunu unutmayın. Böylece tamsayı ve ondalık kısımlar arasında ayırıcı olarak nokta veya virgülle biçimlendirilmiş sayılar yazabilirler. Yuvarlak Dize'nin istediğiniz biçimde olduğundan emin olmak için java.text.NumberFormat öğesini şu şekilde kullanın:

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

İngilizce yerel ayarlarında yazdırır (yerel ayarınız ne olursa olsun): 0.99 123.57 123.00

Örnek Farenda'dan alınmıştır - çiftin Dize'ye nasıl doğru dönüştürüleceği .


0

Genel olarak, yuvarlama ölçeklendirme ile yapılır: 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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.