truncar round numeros limitar float decimales con java decimal rounding digits

round - numeros decimales en java



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

@Milhous: el formato decimal para redondear es excelente:

También puedes usar el

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

para asegurarse de que tiene los 0 finales.

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

Hipotético: tiene que implementar un mecanismo de redondeo en un programa GUI. Para modificar la precisión de un resultado, simplemente cambie el formato de intercalación (es decir, entre 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 similar ("# 0. ############" y usted ingresa un decimal, por ejemplo, 3.1415926, por el bien del argumento, DecimalFormat no produce ninguna basura ( por ejemplo, ceros finales) y devolverá: 3.1415926 .. si así lo desea. Por supuesto, 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.]

Esencialmente, la belleza de DecimalFormat es que maneja simultáneamente el aspecto de la cuerda, así como el nivel de precisión de redondeo establecido. Ergo: obtienes dos beneficios por el precio de una implementación de código. ;)

Lo que me gustaría es un método para convertir un doble en una cadena que se redondea utilizando el método de media altura, es decir, si el decimal a redondear es 5, siempre se redondea al número anterior. Este es el método estándar de redondeo que la mayoría de las personas 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 cero 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 que 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 el DecimalFormatter :

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

devoluciones:

0.91238

Sin embargo, como se puede ver, esto utiliza el redondeo a medias Es decir, se 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?


Aquí hay un resumen de lo que puede usar si desea el 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();

Aquí hay una sugerencia de qué bibliotecas puede usar si quiere double como resultado. Sin embargo, no lo recomendaría para la conversión de cadenas, ya que es posible que el doble no pueda representar exactamente lo que quiere (vea, por ejemplo, here ):

  1. Precision de Apache Commons Math

    double rounded = Precision.round(0.912385, 5, BigDecimal.ROUND_HALF_UP);

  2. Functions de Colt

    double rounded = Functions.round(0.00001).apply(0.912385)

  3. Utils de Weka

    double rounded = Utils.roundDouble(0.912385, 5)


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 el número de decimales.


Como han señalado otros, la respuesta correcta es usar DecimalFormat o BigDecimal . El punto flotante no tiene 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 ejemplo contrario a todas las respuestas en este hilo y, de hecho, en todo (y en otros lugares) que recomiendan la multiplicación seguida de un truncamiento seguido de una división. Corresponde a los defensores de esta técnica explicar por qué el siguiente código produce un 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 programa:

10001 trials 9251 errors

EDITAR: Para abordar algunos comentarios a continuación, rehago 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


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

  • Formato : formatee fácilmente una cadena doble con un cierto número de decimales
  • Análisis : Analice el valor formateado de nuevo al 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), ""); } }


El manual de instrucciones de Java de Real posts esta solución, que también es compatible con versiones anteriores a Java 1.6.

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


Podrías 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; }


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.


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

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

La página de inicio de Apache Commons Mathematics Library 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; } } }



Puedes usar la clase DecimalFormat.

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


Si está utilizando DecimalFormat para convertir 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 enumeración de RoundingMode para seleccionar, dependiendo del comportamiento que requiera.


Si realmente quiere números decimales para el cálculo (y no solo para la salida), no use un formato de punto flotante basado en binario 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, encuentro que el análisis de doble o entero a largo es suficiente para cálculos con números muy grandes.

De hecho, recientemente he usado Parsed-to-Long para obtener representaciones precisas (en lugar de resultados hexadecimales) en una GUI para números tan grandes como ################## ############### caracteres (como ejemplo).


Supongamos que tienes

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


También puedes usar el

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

para asegurarse de que tiene los 0 finales.


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

Véase también https://.com/a/22186845/212950 Gracias a jpdymond por ofrecer esto.


Use setRoundingMode , configure el RoundingMode explícita para manejar su problema con la ronda a medias, 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


Vine aquí solo por una respuesta simple sobre cómo redondear un número. Esta es una respuesta complementaria 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. Se redondea un valor de .5 . Si necesita un comportamiento de redondeo diferente al de ese, puede usar una de las otras funciones Math . Vea la comparación a continuación.

round

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

ceil

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

floor

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

rint

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


En general, el redondeo se realiza mediante escalado: 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


Para lograr esto podemos usar este formateador:

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

o:

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

Usa 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.


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

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

O

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

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


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;


El fragmento de código a continuación muestra cómo mostrar n dígitos. El truco es establecer la variable pp a 1 seguido de n ceros. En el siguiente ejemplo, el valor de la variable pp 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();


Estoy de acuerdo con la respuesta elegida para utilizar DecimalFormat--- o alternativamente BigDecimal.

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

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

La DoubleRounderutilidad en la biblioteca decimal4j proporciona un método similar pero rápido y sin 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);

Saldrá

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 @iaforek señaló que DoubleRounder a veces devuelve resultados contraintuitivos. La razón es que realiza 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 de 256.025 y, por lo tanto, se redondeará hacia abajo.

Notas:

  • Este comportamiento es muy similar al del BigDecimal(double)constructor (pero no al valueOf(double)que usa el constructor de cadenas).
  • El problema puede ser evitado con un paso de doble redondeo a una precisión más alta primero, 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 .


Si consideras 5 o n número de decimal. Puede ser esta respuesta resolver su problema.

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

La salida será: 123.0 1
esto se puede resolver con bucle y función recursiva.


Si está utilizando una tecnología que tiene un mínimo de JDK. Aquí hay una manera sin ningún libs de Java:

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


Tenga en cuenta que String.format () y DecimalFormat producen cadenas usando la configuración regional predeterminada. Por lo tanto, pueden escribir un número formateado con un punto o una coma como separador entre las partes enteras y decimales. Para asegurarse de que la Cadena redondeada está en el formato que desea, use java.text.NumberFormat así:

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 ubicación): 0.99 123.57 123.00

El ejemplo está tomado de Farenda: cómo convertir el doble en una cadena correctamente .


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


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

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

Programa de muestra:

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