La clase BigDecimal
tiene algunos métodos útiles para garantizar una conversión sin pérdidas:
byteValueExact()
shortValueExact()
intValueExact()
longValueExact()
Sin embargo, los métodos floatValueExact()
y doubleValueExact()
no existen.
Leí el código fuente de OpenJDK para métodos floatValue()
y doubleValue()
. Ambos parecen retroceder Float.parseFloat()
y Double.parseDouble()
, respectivamente, lo que puede devolver un infinito positivo o negativo. Por ejemplo, analizar una cadena de 10,000 9s devolverá un infinito positivo. Según tengo entendido, BigDecimal
no tiene un concepto interno de infinito. Además, analizando una cadena de 100 9s como double
da 1.0E100
, que no es infinito, pero pierde precisión.
¿Qué es una implementación razonable floatValueExact()
y doubleValueExact()
?
Pensé en una double
solución mediante la combinación de BigDecimal.doubleValue()
, BigDecial.toString()
, Double.parseDouble(String)
y Double.toString(double)
, pero se ve desordenado. Quiero preguntar aquí porque puede (¡debe!) Haber una solución más simple.
Para ser claros, no necesito una solución de alto rendimiento.
fuente
Respuestas:
De leer los documentos , todo lo que hace con el
numTypeValueExact
variantes es verificar la existencia de una fracción o si el valor es demasiado grande para el tipo numérico y lanzar excepciones.En cuanto a
floatValue()
ydoubleValue()
, se está realizando una comprobación de desbordamiento similar, pero en lugar de lanzar una excepción, en su lugar, devuelveDouble.POSITIVE_INFINITY
oDouble.NEGATIVE_INFINITY
para dobles yFloat.POSITIVE_INFINITY
oFloat.NEGATIVE_INFINITY
flotantes.Por lo tanto, la implementación más razonable (y más simple) de los
exact
métodos para flotante y doble, simplemente debe verificar si la conversión regresaPOSITIVE_INFINITY
oNEGATIVE_INFINITY
.Además , recuerde que
BigDecimal
fue diseñado para manejar la falta de precisión que proviene del usofloat
odouble
para grandes irracionales, por lo tanto, como comentó @JB Nizet , otra comprobación que puede agregar a lo anterior sería convertir o volver adouble
float
BigDecimal
a ver si todavía obtiene El mismo valor. Esto debería probar que la conversión fue correcta.Así es como se vería este método
floatValueExact()
:El uso de en
compareTo
lugar de loequals
anterior es intencional para no ser demasiado estricto con los controles.equals
solo se evaluará como verdadero cuando los dosBigDecimal
objetos tengan el mismo valor y escala (tamaño de la fracción de la parte decimal), mientras quecompareTo
pasará por alto esta diferencia cuando no importe. Por ejemplo2.0
vs2.00
.fuente
Float.isFinite()
oFloat.isInfinite()
, pero esto es opcional.:)
123.456f
. Supongo que esto se debe al diferente tamaño de significado (mantisa) entre el flotante de 32 bits y el doble de 64 bits. En el código anterior, consigo mejores resultados con:if (new BigDecimal(result, MathContext.DECIMAL32).equals(decimal)) {
. ¿Es este un cambio razonable ... o me falta otro caso de esquina de valores de coma flotante?123.456f
es conceptualmente igual que su ejemplo 1.0E100. Me sorprende que si necesita verificar que la conversión de BigDecimal -> punto flotante binario sea exacta, entonces esa necesidad es el problema; es decir, la conversión no debe contemplarse.