A classe BigDecimal
possui alguns métodos úteis para garantir a conversão sem perdas:
byteValueExact()
shortValueExact()
intValueExact()
longValueExact()
No entanto, métodos floatValueExact()
e doubleValueExact()
não existem.
Eu li o código fonte do OpenJDK para métodos floatValue()
e doubleValue()
. Ambos parecem ter fallback Float.parseFloat()
e Double.parseDouble()
, respectivamente, que podem retornar infinito positivo ou negativo. Por exemplo, analisar uma sequência de 10.000 9s retornará infinito positivo. Pelo que entendi, BigDecimal
não tem um conceito interno de infinito. Além disso, a análise de uma sequência de 100 9s como double
dá 1.0E100
, que não é infinito, mas perde precisão.
O que é uma implementação razoável floatValueExact()
e doubleValueExact()
?
Eu pensei em uma double
solução através da combinação BigDecimal.doubleValue()
, BigDecial.toString()
, Double.parseDouble(String)
e Double.toString(double)
, mas parece confuso. Quero perguntar aqui, porque pode (deve!) Haver uma solução mais simples.
Para ser claro, não preciso de uma solução de alto desempenho.
fonte
Respostas:
Ao ler os documentos , tudo o que faz com as
numTypeValueExact
variantes é verificar a existência de uma parte da fração ou se o valor é muito grande para o tipo numérico e lançar exceções.Quanto a
floatValue()
edoubleValue()
, uma verificação de estouro similar está sendo feita, mas, em vez de lançar uma exceção, ela retornaDouble.POSITIVE_INFINITY
ouDouble.NEGATIVE_INFINITY
para duplas e /Float.POSITIVE_INFINITY
ouFloat.NEGATIVE_INFINITY
flutuantes.Portanto, a implementação mais razoável (e mais simples) dos
exact
métodos float e double deve simplesmente verificar se a conversão retornaPOSITIVE_INFINITY
ouNEGATIVE_INFINITY
.Além disso , lembre-se de que ele
BigDecimal
foi projetado para lidar com a falta de precisão resultante do usofloat
oudouble
de irracionais grandes; portanto, como @JB Nizet comentou , outra verificação que você pode adicionar ao acima foi converterdouble
oufloat
voltarBigDecimal
para ver se você ainda consegue o mesmo valor. Isso deve provar que a conversão estava correta.Aqui está como esse método seria
floatValueExact()
:O uso de em
compareTo
vez deequals
acima é intencional, para não se tornar muito rigoroso com as verificações.equals
será avaliado apenas como verdadeiro quando os doisBigDecimal
objetos tiverem o mesmo valor e escala (tamanho da parte da fração do decimal), enquantocompareTo
ignorará essa diferença quando isso não for importante. Por exemplo2.0
vs2.00
.fonte
Float.isFinite()
ouFloat.isInfinite()
, mas isso é opcional.:)
123.456f
. Eu acho que isso é devido ao tamanho diferente de significand (mantissa) entre float de 32 bits e duplo de 64 bits. Em seu código acima, eu obter melhores resultados com:if (new BigDecimal(result, MathContext.DECIMAL32).equals(decimal)) {
. É uma mudança razoável ... ou estou perdendo outro caso de canto de valores de ponto flutuante?123.456f
é conceitualmente o mesmo que o seu exemplo 1.0E100. Parece-me que, se você precisar verificar se a conversão do BigDecimal -> ponto flutuante binário é exata, essa necessidade é o problema; isto é, a conversão não deve ser contemplada.