2011-01-25 57 views
33

Tengo un valor doble = 1.068879335 Quiero redondearlo con solo dos valores decimales como 1.07.Valor doble para redondear en Java

Me trataron como esto

DecimalFormat df=new DecimalFormat("0.00"); 
String formate = df.format(value); 
double finalValue = Double.parseDouble(formate) ; 

esto me está dando esta excepción siguiente

java.lang.NumberFormatException: For input string: "1,07" 
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1224) 
    at java.lang.Double.parseDouble(Double.java:510) 

puede alguien me diga lo que está mal en mi código.

finaly necesito el finalValue = 1.07;

+1

si redonda esto, 1.085879335, precisión 2 dígitos que será 1.09 y 1.08 si redondeo hacia abajo. ¿Cómo es que quieres obtener 1.07 de 1.085879335? – fmucar

Respuesta

75

Tenga en cuenta la coma en su cadena: "1,07". DecimalFormat usa una cadena separadora específica de la configuración regional, mientras que Double.parseDouble() no. Como usted vive en un país donde el separador decimal es ",", no puede analizar su número.

Sin embargo, puede utilizar el mismo DecimalFormat para analizar de nuevo:

DecimalFormat df=new DecimalFormat("0.00"); 
String formate = df.format(value); 
double finalValue = (Double)df.parse(formate) ; 

Pero lo que realmente debe hacer esto en su lugar:

double finalValue = Math.round(value * 100.0)/100.0; 

Nota: Como se ha señalado, se solo debe usar el punto flotante si no necesita un control preciso sobre la precisión. (Los cálculos financieros son el principal ejemplo de cuando no para usarlos)

+2

+1 para identificar realmente el problema. También puede indicar explícitamente a DecimalFormat que use la configuración regional C. – DJClayworth

+0

Como señalé en mi respuesta, este algoritmo generalmente no funciona. Y mientras Java se adhiera al punto flotante IEEE, ese algoritmo no existe. – Waldheinz

+0

@Waldheinz Dado el contexto de la pregunta, mi respuesta es correcta. Juego limpio para que piense un paso más, pero ¿debería saber su respuesta porque no pudo identificar por qué el código falló en primer lugar? Por supuesto que no debería. – biziclop

1

Usted podría intentar definir un nuevo DecimalFormat y que sirva como resultado doble a una nueva variable doble.

Ejemplo dado para hacerte entender lo que acabo de decir.

double decimalnumber = 100.2397; 
DecimalFormat dnf = new DecimalFormat("#,###,###,##0.00"); 
double roundednumber = new Double(dnf.format(decimalnumber)).doubleValue(); 
1

Esto no es posible de la manera solicitada porque hay números con dos decimales que no se pueden expresar exactamente usando números de coma flotante IEEE (para ejemplo 1/10 = 0.1 no se puede expresar como Double o Float). El formateo siempre debe ocurrir como el último paso antes de presentar el resultado al usuario.

Supongo que está preguntando porque quiere tratar con los valores monetarios. No hay forma de hacer esto confiablemente con números de coma flotante, debería considerar cambiar a aritmética de punto fijo. Esto probablemente significa hacer todos los cálculos en "centavos" en lugar de "dólares".

+0

Buen punto, si esto es realmente un cálculo monetario, entonces el punto flotante es del tipo de datos equivocado. – biziclop

+0

¿Podría explicar el examen? ¿Por qué no puedes representar 1/10 como 0.10? –

+3

1/10 es 0.10 pero el punto flotante es binario, no existe tal como 1/10 o 0.1. Solo tiene 1/2, 1/4, 1/8 etc. y cualquier combinación de estos para aproximarse a 0.1 –

6

El problema es que utiliza un formateador de localización que genera un punto decimal específico del entorno local, que es "," en su caso. Pero Double.parseDouble() espera doble literal no localizado. Puede resolver su problema utilizando un método de análisis específico de la configuración regional o cambiando la configuración regional de su formateador a algo que use "." como el punto decimal. O mejor aún, evitar la innecesaria formato mediante el uso de algo como esto:

double rounded = (double) Math.round(value * 100.0)/100.0; 
+1

¿Qué tal esto con 23.8764367843 => 23.88000000002? Creo que un procesamiento de Cadenas es mejor. –

+0

@Manidip, bueno, no hay manera de obtener exactamente 23.88 al analizar "23.88" como el doble tampoco, ya que 23.88 no es exactamente representable en binario de todos modos. Si se necesita una representación exacta, se debe usar algún tipo de biblioteca de precisión arbitraria. –

+0

Tienes razón, pero pensé que estábamos hablando de representación "textual" y "aproximada", no binaria. Waldheinz y Peter respondieron eso arriba. DecimalFormat es una buena opción, pero en caso de que no funcione, probaría un String roundedStr = String.valueOf (redondeado); int puntoPoint = redondeadoStr.indexOf ("."); return (roundedStr) .substring (0, dotPoint <0? roundedStr.length(): dotPoint + 2); Esto no es totalmente correcto, es posible que tenga que agregar los "O" de relleno a la derecha si solo hay 1 dígito a la derecha del punto decimal en RoundedStr, pero tiene la idea de lo que estaba comentando anteriormente. –

4

Hay algo fundamentalmente equivocado en lo que estás tratando de hacer. Los valores binarios de coma flotante no tienen lugares decimales. No se puede redondear significativamente uno a un número dado de decimales, porque la mayoría de los valores decimales "redondos" simplemente no se pueden representar como una fracción binaria.Por eso, uno nunca debe usar float o double para representar dinero.

así que si quieres decimales en el resultado, ese resultado debe ser un String (que ya tienes con el DecimalFormat), o una BigDecimal (que tiene un método setScale() que hace exactamente lo que quiere). De lo contrario, el resultado no puede ser el que quieres que sea.

Lea The Floating-Point Guide para obtener más información.

+1

¿Podríamos imaginar que había pedido "el número de coma flotante más cercano al valor redondeado a dos decimales"? – DJClayworth

+0

@DJClayworth: ¿dónde podría ser útil? –

18

Solución de Live @ Sergey pero con división entera.

double value = 23.8764367843; 
double rounded = (double) Math.round(value * 100)/100; 
System.out.println(value +" rounded is "+ rounded); 

impresiones

23.8764367843 rounded is 23.88 

EDIT: Como Sergey señala, no debería haber ninguna diferencia entre multipling doble * int * y doble doble y dividiendo doble/int y doble/doble. No puedo encontrar un ejemplo donde el resultado sea diferente. Sin embargo, en x86/x64 y otros sistemas existe una instrucción de código de máquina específica para valores mixtos dobles, que creo que utiliza la JVM.

for (int j = 0; j < 11; j++) { 
    long start = System.nanoTime(); 
    for (double i = 1; i < 1e6; i *= 1.0000001) { 
     double rounded = (double) Math.round(i * 100)/100; 
    } 
    long time = System.nanoTime() - start; 
    System.out.printf("double,int operations %,d%n", time); 
} 
for (int j = 0; j < 11; j++) { 
    long start = System.nanoTime(); 
    for (double i = 1; i < 1e6; i *= 1.0000001) { 
     double rounded = (double) Math.round(i * 100.0)/100.0; 
    } 
    long time = System.nanoTime() - start; 
    System.out.printf("double,double operations %,d%n", time); 
} 

imprime

double,int operations 613,552,212 
double,int operations 661,823,569 
double,int operations 659,398,960 
double,int operations 659,343,506 
double,int operations 653,851,816 
double,int operations 645,317,212 
double,int operations 647,765,219 
double,int operations 655,101,137 
double,int operations 657,407,715 
double,int operations 654,858,858 
double,int operations 648,702,279 
double,double operations 1,178,561,102 
double,double operations 1,187,694,386 
double,double operations 1,184,338,024 
double,double operations 1,178,556,353 
double,double operations 1,176,622,937 
double,double operations 1,169,324,313 
double,double operations 1,173,162,162 
double,double operations 1,169,027,348 
double,double operations 1,175,080,353 
double,double operations 1,182,830,988 
double,double operations 1,185,028,544 
+1

¿Dónde está la división de enteros? El operador de transmisión tiene mayor precedencia, por lo que divide un doble por un entero, pero el entero se promueve al doble en dicho caso. Por lo tanto, es esencialmente lo mismo que mi código. O lo mismo que 'Math.round (value * 100)/100.0'. Es solo una cuestión de estilo. Si utilizó una división entera genuina, obtendría 23 como resultado. –

+0

@Sergey, en teoría, tiene razón y tal vez mi respuesta es específica de JVM, consulte mi edición para obtener más detalles. –

+0

impresionante. El JLS establece claramente que el entero debe ser promovido al doble en dicha división, por lo que el resultado debe ser el mismo para ambos casos en cualquier implementación de Java conforme, pero no tenía idea de que JVM optimiza la implementación real de esa manera. –

1
double TotalPrice=90.98989898898; 

    DecimalFormat format_2Places = new DecimalFormat("0.00"); 

    TotalPrice = Double.valueOf(format_2Places.format(TotalPrice)); 
0

Usted puede utilizar el formato como here,

public static double getDoubleValue(String value,int digit){ 
    if(value==null){ 
     value="0"; 
    } 
    double i=0; 
    try { 
     DecimalFormat digitformat = new DecimalFormat("#.##"); 
     digitformat.setMaximumFractionDigits(digit); 
     return Double.valueOf(digitformat.format(Double.parseDouble(value))); 

    } catch (NumberFormatException numberFormatExp) { 
     return i; 
    } 
} 
2

Prueba 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

Apache Commons página de inicio Matemáticas Biblioteca 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; 
     } 
    } 
} 
Cuestiones relacionadas