value Como arredondar um número para n casas decimais em Java




set decimal precision java (24)

O trecho de código abaixo mostra como exibir n dígitos. O truque é definir a variável pp para 1, seguida de n zeros. No exemplo abaixo, o valor da variável pp tem 5 zeros, então 5 dígitos serão exibidos.

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

O que eu gostaria é um método para converter um double em uma string que arredonda usando o método half-up - ou seja, se o decimal a ser arredondado é 5, ele sempre arredonda para o número anterior. Esse é o método padrão de arredondamento que a maioria das pessoas espera na maioria das situações.

Eu também gostaria que apenas dígitos significativos fossem exibidos - ou seja, não deveria haver nenhum zeros à direita.

Eu sei que um método de fazer isso é usar o método String.format :

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

retorna:

0.91239

o que é ótimo, no entanto, sempre exibe números com 5 casas decimais, mesmo que não sejam significativas:

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

retorna:

0.91230

Outro método é usar o DecimalFormatter :

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

retorna:

0.91238

No entanto, como você pode ver isso usa arredondamento de meia-par. Isso é arredondar para baixo se o dígito anterior é par. O que eu gostaria é isto:

0.912385 -> 0.91239
0.912300 -> 0.9123

Qual é a melhor maneira de conseguir isso em Java?


Desde que eu não encontrei nenhuma resposta completa sobre este tema eu coloquei uma classe que deve lidar com isso corretamente, com suporte para:

  • Formatação : formate facilmente um duplo para string com um certo número de casas decimais
  • Análise : Analisar o valor formatado de volta para o dobro
  • Localidade : formatar e analisar usando a localidade padrão
  • Notação exponencial : Comece a usar a notação exponencial após um certo limite

O uso é bem simples :

(Por causa deste exemplo, estou usando uma localidade personalizada)

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

Aqui está a aula :

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

}

Se você estiver usando uma tecnologia que tenha um mínimo de JDK. Aqui está uma maneira sem quaisquer bibliotecas Java:

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

Você também pode usar o

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

para ter certeza de que você tem o 0 à direita.


Tente isto: org.apache.commons.math3.util.Precision.round (double x, int scale)

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

A página inicial da Apache Commons Mathematics Library é: http://commons.apache.org/proper/commons-math/index.html

A implementação interna deste método é:

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

Apenas no caso de alguém ainda precisar de ajuda com isso. Esta solução funciona perfeitamente para mim.

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

}

retorna um String com a saída desejada.


DecimalFormat é a melhor maneira de produzir, mas eu não prefiro isso. Eu sempre faço isso o tempo todo, porque retorna o valor duplo. Então eu posso usar mais do que apenas saída.

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

OU

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

Se você precisar de valores de casas decimais grandes, poderá usar BigDecimal. De qualquer maneira, 0 é importante. Sem ele, o arredondamento de 0.33333d5 retorna 0.33333 e apenas 9 dígitos são permitidos. A segunda função sem .0 tem problemas com 0,30000 de retorno 0,30000000000000004.


Como alguns outros notaram, a resposta correta é usar DecimalFormat ou BigDecimal . O ponto flutuante não tem casas decimais, portanto você não pode arredondar / truncar para um número específico delas em primeiro lugar. Você tem que trabalhar em uma raiz decimal, e é isso que essas duas classes fazem.

Eu estou postando o código a seguir como um contra-exemplo para todas as respostas neste segmento e na verdade todo (e em outros lugares) que recomendam a multiplicação seguido por truncamento seguido por divisão. Cabe aos defensores dessa técnica explicar por que o código a seguir produz a saída errada em mais de 92% dos casos.

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

Saída deste programa:

10001 trials 9251 errors

EDIT: Para resolver alguns comentários abaixo eu refiz a parte de módulo do loop de teste usando BigDecimal e new MathContext(16) para a operação de módulo da seguinte maneira:

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

Resultado:

10001 trials 4401 errors

Você pode usar o seguinte método utilitário

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

Use setRoundingMode , defina setRoundingMode explicitamente para lidar com o problema com a rodada de meia-série e use o padrão de formato para a saída desejada.

Exemplo:

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

dá a saída:

12
123.1235
0.23
0.1
2341234.2125

Se você realmente quiser números decimais para cálculo (e não apenas para saída), não use um formato de ponto flutuante baseado em binário como o dobro.

Use BigDecimal or any other decimal-based format.

Eu uso BigDecimal para cálculos, mas tenha em mente que é dependente do tamanho dos números que você está lidando. Na maioria das minhas implementações, acho que a análise de double ou integer para Long é suficiente para cálculos numéricos muito grandes.

Na verdade, usei parsed-to-Long recentemente para obter representações precisas (em oposição a resultados hexadecimais) em uma GUI para números tão grandes quanto ################## ############### caracteres (como um exemplo).


Se você estiver usando o DecimalFormat para converter o double em String , é muito simples:

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

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

Existem vários valores de enum de RoundingMode para selecionar, dependendo do comportamento desejado.


Concordo com a resposta escolhida para usar DecimalFormat --- ou, em alternativa, BigDecimal .

Por favor, leia Atualização abaixo primeiro!

No entanto, se você quiser arredondar o valor duplo e obter um resultado com valor double , poderá usar org.apache.commons.math3.util.Precision.round(..) conforme mencionado acima. A implementação usa BigDecimal , é lenta e cria lixo.

Um método semelhante, mas rápido e sem lixo, é fornecido pelo utilitário DoubleRounder na biblioteca 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);

Saída

 0.667
 0.666
 1000.0
 9.00800700601E10

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

Disclaimer: Estou envolvido no projeto decimal4j.

Update: Como @iaforek apontou DoubleRounder, por vezes, retorna resultados contra-intuitivos. A razão é que ele realiza um arredondamento matematicamente correto. Por exemplo, DoubleRounder.round(256.025d, 2) será arredondado para 256.02 porque o valor double representado como 256.025d é um pouco menor que o valor racional 256.025 e, portanto, será arredondado para baixo.

Notas:

  • Esse comportamento é muito semelhante ao do construtor BigDecimal(double) (mas não para valueOf(double) que usa o construtor de string).
  • O problema pode ser contornado com um passo de arredondamento duplo para uma precisão maior primeiro, mas é complicado e eu não vou entrar nos detalhes aqui

Por essas razões e tudo mencionado acima neste post não posso recomendar o uso do DoubleRounder .


Aqui está um resumo do que você pode usar se você quiser o resultado como 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();

Aqui está uma sugestão de quais bibliotecas você pode usar se quiser double como resultado. Eu não recomendaria isso para a conversão de strings, já que o dobro pode não ser capaz de representar exatamente o que você quer (veja, por exemplo, here ):

  1. Precision do Apache Commons Math

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

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

    double rounded = Utils.roundDouble(0.912385, 5)

Onde dp = casa decimal que você quer, e o valor é um duplo.

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

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

Suponha que você tenha

double d = 9232.129394d;

você pode usar BigDecimal

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

ou sem BigDecimal

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

com ambas as soluções d == 9232.13


Em geral, o arredondamento é feito pelo dimensionamento: 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

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

você obterá um BigDecimal . Para obter a string, basta chamar o método toString BigDecimal , ou o método toPlainString para Java 5+ para uma string de formato simples.

Programa de amostra:

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

Tenha em mente que String.format () e DecimalFormat produzem string usando Locale padrão. Assim, eles podem escrever um número formatado com ponto ou vírgula como um separador entre partes inteiras e decimais. Para certificar-se de que a String arredondada esteja no formato desejado, use java.text.NumberFormat da seguinte forma:

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

Vai imprimir em inglês local (não importa qual é sua localidade): 0,99 123,57 123,00

O exemplo é retirado de Farenda - como converter duas vezes em string corretamente .


@Milhous: o formato decimal para arredondamento é excelente:

Você também pode usar o

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

para ter certeza de que você tem o 0 à direita.

Gostaria de acrescentar que este método é muito bom em fornecer um mecanismo numérico real de arredondamento - não apenas visualmente, mas também durante o processamento.

Hipotética: você precisa implementar um mecanismo de arredondamento em um programa GUI. Para alterar a exatidão / precisão de uma saída de resultado, basta alterar o formato do cursor (ou seja, dentro dos colchetes). De modo a:

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

retornaria como saída: 0.912385

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

retornaria como saída: 0.91239

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

retornaria como saída: 0.9124

[EDIT: também se o formato de circunflexo é assim ("# 0. ############") e você digitar um decimal, por exemplo, 3,1415926, por causa do argumento, DecimalFormat não produz qualquer lixo ( por exemplo, zeros à direita) e irá retornar: 3.1415926 .. se você está assim inclinado. Concedido, é um pouco detalhado para o gosto de alguns desenvolvedores - mas, ei, ele tem pouca memória durante o processamento e é muito fácil de implementar.]

Então, essencialmente, a beleza do DecimalFormat é que ele lida simultaneamente com a aparência da string - bem como o nível de precisão do arredondamento definido. Ergo: você obtém dois benefícios pelo preço de uma implementação de código. ;)


Assumindo que o value é um double , você pode fazer:

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

Isso é para precisão de 5 dígitos. O número de zeros indica o número de decimais.


Para conseguir isso, podemos usar este formatador:

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

ou:

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

Use este método para obter sempre duas casas decimais:

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

Definindo esses valores:

91.32
5.22
11.5
1.2
2.6

Usando o método, podemos obter os seguintes resultados:

91.32
5.22
11.50
1.20
2.60

demonstração online.


O Java How-to da Real posts esta solução, que também é compatível com versões anteriores ao Java 1.6.

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

Você pode usar a classe DecimalFormat.

double d = 3.76628729;

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






digits