truncar round redondear limitar float decimales con java floating-point decimal

java - round - ¿Cómo verificar si un doble tiene como máximo n lugares decimales?



redondear decimales en java netbeans (7)

Al igual que con toda la aritmética de punto flotante, no debe verificar la igualdad, sino que el error (épsilon) es lo suficientemente pequeño.

Si reemplazas:

return (d==check);

con algo así como

return (Math.abs(d-check) <= 0.0000001);

deberia de funcionar. Obviamente, el épsilon debe ser lo suficientemente pequeño en comparación con el número de decimales que está buscando.

Actualmente tengo este método:

static boolean checkDecimalPlaces(double d, int decimalPlaces){ if (d==0) return true; double multiplier = Math.pow(10, decimalPlaces); double check = d * multiplier; check = Math.round(check); check = check/multiplier; return (d==check); }

Pero este método falla para checkDecmialPlaces(649632196443.4279, 4) probablemente porque base 10 matemáticas en un número base 2.

Entonces, ¿cómo se puede hacer esta verificación correctamente?

Pensé en obtener una representación de cadena del doble valor y luego verificarlo con una expresión regular, pero me pareció extraño.

EDITAR: Gracias por todas las respuestas. Hay casos en los que realmente obtengo un doble y para esos casos implementé lo siguiente:

private static boolean checkDecimalPlaces(double d, int decimalPlaces) { if (d == 0) return true; final double epsilon = Math.pow(10.0, ((decimalPlaces + 1) * -1)); double multiplier = Math.pow(10, decimalPlaces); double check = d * multiplier; long checkLong = (long) Math.abs(check); check = checkLong / multiplier; double e = Math.abs(d - check); return e < epsilon; }

Cambié la round a un truncamiento. Parece que el cálculo realizado en round aumenta demasiado la imprecisión. Al menos en el caso de prueba que falla.
Como algunos de ustedes señalaron que si podía acceder a la entrada de cadena ''real'', debería usar BigDecimal para verificar y así lo hice:

BigDecimal decimal = new BigDecimal(value); BigDecimal checkDecimal = decimal.movePointRight(decimalPlaces); return checkDecimal.scale() == 0;

El double valor que obtengo proviene de la API de POI de Apache que lee los archivos de Excel. Hice algunas pruebas y descubrí que, aunque la API devuelve valores double para las celdas numéricas, puedo obtener una representación precisa cuando formateo inmediatamente ese double con DecimalFormat :

DecimalFormat decimalFormat = new DecimalFormat(); decimalFormat.setMaximumIntegerDigits(Integer.MAX_VALUE); // don''t use grouping for numeric-type cells decimalFormat.setGroupingUsed(false); decimalFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US)); value = decimalFormat.format(numericValue);

Esto también funciona para valores que no se pueden representar exactamente en formato binario.


El tipo double es un número de punto flotante binario. Siempre hay aparentes inexactitudes al tratar con ellos como si fueran números de punto flotante decimal. No sé si alguna vez podrá escribir su función para que funcione de la manera que desee.

Es probable que tenga que volver a la fuente original del número (una entrada de cadena quizás) y conservar la representación decimal si es importante para usted.


No estoy seguro de que esto sea realmente factible en general. Por ejemplo, ¿cuántos lugares decimales tiene 1.0e-13 ? ¿Qué pasa si resulta de un error de redondeo al hacer aritmética y realmente está solo 0 disfrazada? Si está activado, por otro lado, está preguntando si hay dígitos que no sean cero en los primeros n lugares decimales, puede hacer algo como:

static boolean checkDecimalPlaces(double d, unsigned int decimalPlaces){ // take advantage of truncation, may need to use BigInt here // depending on your range double d_abs = Math.abs(d); unsigned long d_i = d_abs; unsigned long e = (d_abs - d_i) * Math.pow(10, decimalPlaces); return e > 0; }


Si puede cambiar a BigDecimal, entonces, como explica Ken G, eso es lo que debería estar usando.

De lo contrario, tendrá que lidiar con una serie de problemas como se menciona en las otras respuestas. Para mí, estás tratando con un número binario (doble) y haciendo una pregunta sobre una representación decimal de ese número; es decir, preguntas sobre una Cadena. Creo que tu intuición es correcta.


Si su objetivo es representar un número con exactamente n cifras significativas a la derecha del decimal, BigDecimal es la clase a usar.

Números decimales firmados de precisión arbitraria e inmutable. Un BigDecimal consiste en un valor sin escalar entero de precisión arbitraria y una escala entera de 32 bits. Si es cero o positivo, la escala es el número de dígitos a la derecha del punto decimal. Si es negativo, el valor sin escala del número se multiplica por diez a la potencia de la negación de la escala. El valor del número representado por BigDecimal es, por lo tanto, (unscaledValue × 10-scale).

scale se puede establecer a través de setScale (int)


Creo que esto es mejor Convertir a cadena e interrogar el valor para el exponente

public int calcBase10Exponet (Number increment) { //toSting of 0.0=0.0 //toSting of 1.0=1.0 //toSting of 10.0=10.0 //toSting of 100.0=100.0 //toSting of 1000.0=1000.0 //toSting of 10000.0=10000.0 //toSting of 100000.0=100000.0 //toSting of 1000000.0=1000000.0 //toSting of 1.0E7=1.0E7 //toSting of 1.0E8=1.0E8 //toSting of 1.0E9=1.0E9 //toSting of 1.0E10=1.0E10 //toSting of 1.0E11=1.0E11 //toSting of 0.1=0.1 //toSting of 0.01=0.01 //toSting of 0.0010=0.0010 <== need to trim off this extra zero //toSting of 1.0E-4=1.0E-4 //toSting of 1.0E-5=1.0E-5 //toSting of 1.0E-6=1.0E-6 //toSting of 1.0E-7=1.0E-7 //toSting of 1.0E-8=1.0E-8 //toSting of 1.0E-9=1.0E-9 //toSting of 1.0E-10=1.0E-10 //toSting of 1.0E-11=1.0E-11 double dbl = increment.doubleValue (); String str = Double.toString (dbl); // System.out.println ("NumberBoxDefaultPatternCalculator: toSting of " + dbl + "=" + str); if (str.contains ("E")) { return Integer.parseInt (str.substring (str.indexOf ("E") + 1)); } if (str.endsWith (".0")) { return str.length () - 3; } while (str.endsWith ("0")) { str = str.substring (0, str.length () - 1); } return - (str.length () - str.indexOf (".") - 1); }


La prueba falla, porque ha alcanzado la precisión de la representación del punto flotante binario, que es de aproximadamente 16 dígitos con doble precisión IEEE754 . Multiplicar por 649632196443.4279 por 10000 truncará la representación binaria, lo que provocará errores al redondear y dividir después, lo que invalidará por completo el resultado de su función.

Para obtener más detalles, consulte http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

Una mejor manera sería verificar si los decimales n+1 están por debajo de un cierto umbral. Si d - round(d) es menor que epsilon (ver límite ), la representación decimal de d no tiene lugares decimales significativos. De manera similar si (d - round(d)) * 10^n es menor que epsilon , d puede tener como máximo n lugares significativos.

Utilice el DoubleConverter Jon Skeet para verificar los casos en que d no sea lo suficientemente preciso como para contener los decimales que está buscando.