Cómo redondear un número a n lugares decimales en Java

Lo que me gustaría es un método para convertir un doble en una cadena que redondea usando el método de la mitad hacia arriba, es decir, si el decimal que se redondeará es 5, siempre se redondea al número anterior. Este es el método estándar de redondeo que la mayoría de la gente espera en la mayoría de las situaciones.

También me gustaría que solo se mostraran dígitos significativos, es decir, no debería haber ningún cerco final.

Sé que un método para hacer esto es usar el método String.format :

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

devoluciones:

 0.91239 

Lo cual es genial, sin embargo, siempre muestra números con 5 lugares decimales, incluso si no son significativos:

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

devoluciones:

 0.91230 

Otro método es usar DecimalFormatter :

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

devoluciones:

 0.91238 

Sin embargo, como puede ver, esto utiliza el redondeo a la mitad o incluso el redondeo. Es decir redondeará hacia abajo si el dígito anterior es par. Lo que me gustaría es esto:

 0.912385 -> 0.91239 0.912300 -> 0.9123 

¿Cuál es la mejor manera de lograr esto en Java?

Use setRoundingMode , establezca RoundingMode explícitamente para manejar su problema con la ronda de medios pares, luego use el patrón de formato para su salida requerida.

Ejemplo:

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

da la salida:

 12 123.1235 0.23 0.1 2341234.2125 

Asumiendo que el value es un double , puedes hacer:

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

Eso es para 5 dígitos de precisión. El número de ceros indica la cantidad de decimales.

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

te conseguirá un BigDecimal . Para sacar la cadena, simplemente llame al método BigDecimal ‘s toString , o al método toPlainString para Java 5+ para una cadena de formato simple.

Progtwig de ejemplo:

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

También puedes usar el

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

para asegurarse de tener los 0 finales.

Como han señalado otros, la respuesta correcta es usar DecimalFormat o BigDecimal . El punto flotante no tiene lugares decimales, por lo que no es posible redondear / truncar a un número específico de ellos en primer lugar. Tienes que trabajar en una base decimal, y eso es lo que hacen esas dos clases.

Estoy publicando el siguiente código como un contraejemplo para todas las respuestas en este hilo y, de hecho, en todo StackOverflow (y en otros lugares) que recomiendan multiplicación seguida de truncamiento seguido de división. Incumbe a los defensores de esta técnica explicar por qué el siguiente código produce el resultado incorrecto en más del 92% de los 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"); } } 

Salida de este progtwig:

 10001 trials 9251 errors 

EDITAR: Para abordar algunos comentarios a continuación redirijé la parte del módulo del bucle de prueba usando BigDecimal y el new MathContext(16) para la operación del módulo de la siguiente manera:

 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 

Supongamos que tiene

 double d = 9232.129394d; 

puedes usar BigDecimal

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

o sin BigDecimal

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

con ambas soluciones d == 9232.13

Puede usar la clase DecimalFormat.

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

Real de Java How-to publica esta solución, que también es compatible para versiones anteriores a Java 1.6.

 BigDecimal bd = new BigDecimal(Double.toString(d)); bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP); return bd.doubleValue(); 
 double myNum = .912385; int precision = 10000; //keep 4 digits myNum= Math.floor(myNum * precision +.5)/precision; 

@Milhous: el formato decimal para el redondeo es excelente:

También puedes usar el

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

para asegurarse de tener los 0 finales.

Añadiría que este método es muy bueno para proporcionar un mecanismo real de redondeo numérico, no solo visualmente, sino también cuando se procesa.

Hipotético: debe implementar un mecanismo de redondeo en un progtwig GUI. Para alterar la precisión / exactitud de una salida de resultados simplemente cambie el formato de intercalación (es decir, dentro de los corchetes). Así que eso:

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

volvería como salida: 0.912385

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

volvería como salida: 0.91239

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

volvería como salida: 0.9124

[EDITAR: también si el formato de intercalación es así (“# 0. ############”) e ingresa un decimal, por ejemplo, 3.1415926, por razones de argumento, DecimalFormat no produce ninguna basura ( por ejemplo, ceros finales) y devolverá: 3.1415926 … si está inclinado de esa manera. De acuerdo, es un poco detallado para el gusto de algunos desarrolladores, pero bueno, tiene una huella de memoria baja durante el procesamiento y es muy fácil de implementar.]

En esencia, la belleza de DecimalFormat es que maneja simultáneamente la apariencia de la cuerda, así como el nivel de precisión de redondeo establecido. Ergo: obtienes dos beneficios por el precio de la implementación de un código. 😉

Puede usar el siguiente método de utilidad:

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

Aquí hay un resumen de lo que puede usar si quiere el resultado como Cadena:

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

Aquí hay una sugerencia de qué bibliotecas puede usar si quiere double resultado. Sin embargo, no lo recomendaría para la conversión de cadenas, ya que el doble puede no ser capaz de representar exactamente lo que desea (ver, por ejemplo, aquí ):

  1. Precisión de Apache Commons Math

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

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

     double rounded = Utils.roundDouble(0.912385, 5) 

Una solución sucinta

  public static double round(double value, int precision) { int scale = (int) Math.pow(10, precision); return (double) Math.round(value * scale) / scale; } 

Ver también, https://stackoverflow.com/a/22186845/212950 Gracias a jpdymond por ofrecer esto.

Puedes usar BigDecimal

 BigDecimal value = new BigDecimal("2.3"); value = value.setScale(0, RoundingMode.UP); BigDecimal value1 = new BigDecimal("-2.3"); value1 = value1.setScale(0, RoundingMode.UP); System.out.println(value + "n" + value1); 

Consulte: http://www.javabeat.net/precise-rounding-of-decimals-using-rounding-mode-enumeration/

Pruebe esto: org.apache.commons.math3.util.Precision.round (doble x, escala int)

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

La página principal de la Biblioteca de Matemáticas de Apache Commons es: http://commons.apache.org/proper/commons-math/index.html

La implementación interna de este método es:

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

Como no encontré una respuesta completa sobre este tema, he creado una clase que debería manejar esto correctamente, con soporte para:

  • Formateo : formatee fácilmente un doble en una cadena con una cierta cantidad de decimales
  • Análisis : Parse el valor formateado de nuevo a doble
  • Configuración regional : formatee y analice utilizando la configuración regional predeterminada
  • Notación exponencial : comience a usar la notación exponencial después de un cierto umbral

El uso es bastante simple :

(Por el bien de este ejemplo, estoy usando una configuración regional 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 

Aquí está la clase :

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

Por si acaso alguien todavía necesita ayuda con esto. Esta solución funciona perfectamente para mí.

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

devuelve una String con la salida deseada.

Si realmente desea números decimales para el cálculo (y no solo para la salida), no use un formato de coma flotante basado en binarios como el doble.

 Use BigDecimal or any other decimal-based format. 

Utilizo BigDecimal para los cálculos, pero tenga en cuenta que depende del tamaño de los números con los que está tratando. En la mayoría de mis implementaciones, considero que el análisis desde doble o entero hasta Long es suficiente para cálculos de números muy grandes.

De hecho, recientemente utilicé el análisis sintáctico para obtener representaciones precisas (en lugar de resultados hexadecimales) en una GUI para números tan grandes como ################## ############### personajes (como un ejemplo).

Si está utilizando DecimalFormat para convertir el double a String , es muy sencillo:

 DecimalFormat formatter = new DecimalFormat("0.0##"); formatter.setRoundingMode(RoundingMode.HALF_UP); double num = 1.234567; return formatter.format(num); 

Hay varios valores de enunciado RoundingMode para seleccionar, dependiendo del comportamiento que requiera.

El siguiente fragmento de código muestra cómo mostrar n dígitos. El truco es establecer la variable pp en 1 seguida de n ceros. En el siguiente ejemplo, el valor de pp variable tiene 5 ceros, por lo que se mostrarán 5 dígitos.

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

Vine aquí solo queriendo una respuesta simple sobre cómo redondear un número. Esta es una respuesta suplementaria para proporcionar eso.

Cómo redondear un número en Java

El caso más común es usar Math.round() .

 Math.round(3.7) // 4 

Los números se redondean al número entero más cercano. Un valor .5 se redondea hacia arriba. Si necesita un comportamiento de redondeo diferente, puede usar una de las otras funciones de Matemáticas . Ver la comparación a continuación.

redondo

Como se indicó anteriormente, esto redondea al número entero más cercano. .5 decimales redondear. Este método devuelve un 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 

hacer techo

Cualquier valor decimal se redondea al siguiente entero. Va al techo . Este método devuelve un 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 

piso

Cualquier valor decimal se redondea al siguiente entero. Este método devuelve un 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

Esto es similar a la ronda en esa ronda de valores decimales al entero más cercano. Sin embargo, a diferencia de los valores round , .5 redondean al entero par. Este método devuelve un 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 *** 

Estoy de acuerdo con la respuesta elegida para usar DecimalFormat — o alternativamente BigDecimal .

Por favor, lea la actualización a continuación primero!

Sin embargo, si desea redondear el doble valor y obtener un resultado de double valor, puede usar org.apache.commons.math3.util.Precision.round(..) como se mencionó anteriormente. La implementación usa BigDecimal , es lenta y crea basura.

La utilidad DoubleRounder en la biblioteca decimal4j proporciona un método similar pero rápido y libre de basura:

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

Producirá

  0.667 0.666 1000.0 9.00800700601E10 

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

Descargo de responsabilidad: Estoy involucrado en el proyecto decimal4j.

Actualización: como señaló @iaforek, DoubleRounder a veces devuelve resultados contradictorios. La razón es que realiza el redondeo matemáticamente correcto. Por ejemplo, DoubleRounder.round(256.025d, 2) se redondeará a 256.02 porque el valor doble representado como 256.025d es algo más pequeño que el valor racional 256.025 y, por lo tanto, se redondeará hacia abajo.

Notas:

  • Este comportamiento es muy similar al del constructor BigDecimal(double) (pero no a valueOf(double) que usa el constructor de cadenas).
  • El problema se puede eludir primero con un doble paso de redondeo a una mayor precisión, pero es complicado y no voy a entrar en detalles aquí

Por esas razones y todo lo mencionado anteriormente en esta publicación, no puedo recomendar el uso de DoubleRounder .

DecimalFormat es la mejor forma de generar resultados, pero no lo prefiero. Siempre hago esto todo el tiempo, porque devuelve el doble valor. Entonces puedo usarlo más que solo salida.

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

O

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

Si necesita valores de decimales grandes, puede usar BigDecimal en su lugar. De todos modos .0 es importante. Sin él, el redondeo de 0.33333d5 devuelve 0.33333 y solo permite 9 dígitos. La segunda función sin .0 tiene problemas con 0.30000 return 0.30000000000000004.

Para lograr esto, podemos usar este formateador:

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

o:

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

Use este método para obtener siempre dos decimales:

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

Definiendo estos valores:

 91.32 5.22 11.5 1.2 2.6 

Usando el método podemos obtener estos resultados:

 91.32 5.22 11.50 1.20 2.60 

demo en línea.

Tenga en cuenta que String.format () y DecimalFormat producen cadenas usando Locale predeterminado. Por lo tanto, pueden escribir el número formateado con punto o coma como un separador entre las partes enteras y decimales. Para asegurarse de que la cadena redondeada está en el formato que desea, utilice java.text.NumberFormat de la siguiente manera:

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

Se imprimirá en la configuración regional en inglés (sin importar cuál sea su configuración regional): 0.99 123.57 123.00

El ejemplo está tomado de Farenda: cómo convertir el doble a Cadena correctamente .

Donde dp = lugar decimal que desea, y el valor es un doble.

  double p = Math.pow(10d, dp); double result = Math.round(value * p)/p; 

Si considera 5 o n número de decimal. May be this answer solve your prob.

  double a = 123.00449; double roundOff1 = Math.round(a*10000)/10000.00; double roundOff2 = Math.round(roundOff1*1000)/1000.00; double roundOff = Math.round(roundOff2*100)/100.00; System.out.println("result:"+roundOff); 

Output will be: 123.0 1
this can be solve with loop and recursive function.

If you’re using a technology that has a minimal JDK. Here’s a way without any Java libs:

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

In general, rounding is done by scaling: 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