Convertendo BigDecimal em Inteiro
-
27-09-2019 - |
Pergunta
Eu tenho o método Hibernate que me retorna um BigDecimal.Tenho outro método API para o qual preciso passar esse número mas ele aceita Integer como parâmetro.Não consigo alterar os tipos de retorno ou tipos de variáveis de ambos os métodos.
Agora, como converter BigDecimal em Integer e passá-lo para o segundo método?
Existe uma maneira de sair disso?
Solução
Você ligaria myBigDecimal.intValueExact()
(ou apenas intValue()
) e até lançará uma exceção se você perder informações.Isso retorna um int, mas o autoboxing cuida disso.
Outras dicas
Você pode garantir que o BigDecimal
nunca conterá um valor maior que Integer.MAX_VALUE
?
Se sim, então aqui está o seu código chamando intValue
:
Integer.valueOf(bdValue.intValue())
DR
Use um destes para necessidades de conversão universal
//Java 7 or below
bigDecimal.setScale(0, RoundingMode.DOWN).intValueExact()
//Java 8
bigDecimal.toBigInteger().intValueExact()
Raciocínio
A resposta depende de quais são os requisitos e como você responde a essas perguntas.
- Será que
BigDecimal
potencialmente tem uma parte fracionária diferente de zero? - Será que
BigDecimal
potencialmente não se enquadra noInteger
faixa? - Você gostaria de partes fracionárias diferentes de zero arredondadas ou truncadas?
- Como você gostaria que as partes fracionárias diferentes de zero fossem arredondadas?
Se você respondeu não às 2 primeiras perguntas, você poderia simplesmente usar BigDecimal.intValueExact()
como outros sugeriram e deixá-lo explodir quando algo inesperado acontecer.
Se você não estiver absolutamente 100% confiante sobre a pergunta número 2, então intValue()
é sempre a resposta errada.
Tornando isso melhor
Vamos usar as seguintes suposições com base nas outras respostas.
- Não há problema em perder precisão e truncar o valor porque é isso que
intValueExact()
e boxe automático fazem - Queremos que uma exceção seja lançada quando o
BigDecimal
é maior que oInteger
intervalo porque qualquer outra coisa seria uma loucura, a menos que você tenha uma necessidade muito específica do que acontece quando você elimina os bits de ordem superior.
Dados esses parâmetros, intValueExact()
lança uma exceção quando não queremos, se nossa parte fracionária for diferente de zero.Por outro lado, intValue()
não lança uma exceção quando deveria se nosso BigDecimal
é muito grande.
Para obter o melhor dos dois mundos, complete o BigDecimal
primeiro e depois converta.Isso também tem a vantagem de lhe dar mais controle sobre o processo de arredondamento.
Teste Spock Groovy
void 'test BigDecimal rounding'() {
given:
BigDecimal decimal = new BigDecimal(Integer.MAX_VALUE - 1.99)
BigDecimal hugeDecimal = new BigDecimal(Integer.MAX_VALUE + 1.99)
BigDecimal reallyHuge = new BigDecimal("10000000000000000000000000000000000000000000000")
String decimalAsBigIntString = decimal.toBigInteger().toString()
String hugeDecimalAsBigIntString = hugeDecimal.toBigInteger().toString()
String reallyHugeAsBigIntString = reallyHuge.toBigInteger().toString()
expect: 'decimals that can be truncated within Integer range to do so without exception'
//GOOD: Truncates without exception
'' + decimal.intValue() == decimalAsBigIntString
//BAD: Throws ArithmeticException 'Non-zero decimal digits' because we lose information
// decimal.intValueExact() == decimalAsBigIntString
//GOOD: Truncates without exception
'' + decimal.setScale(0, RoundingMode.DOWN).intValueExact() == decimalAsBigIntString
and: 'truncated decimal that cannot be truncated within Integer range throw conversionOverflow exception'
//BAD: hugeDecimal.intValue() is -2147483648 instead of 2147483648
//'' + hugeDecimal.intValue() == hugeDecimalAsBigIntString
//BAD: Throws ArithmeticException 'Non-zero decimal digits' because we lose information
//'' + hugeDecimal.intValueExact() == hugeDecimalAsBigIntString
//GOOD: Throws conversionOverflow ArithmeticException because to large
//'' + hugeDecimal.setScale(0, RoundingMode.DOWN).intValueExact() == hugeDecimalAsBigIntString
and: 'truncated decimal that cannot be truncated within Integer range throw conversionOverflow exception'
//BAD: hugeDecimal.intValue() is 0
//'' + reallyHuge.intValue() == reallyHugeAsBigIntString
//GOOD: Throws conversionOverflow ArithmeticException because to large
//'' + reallyHuge.intValueExact() == reallyHugeAsBigIntString
//GOOD: Throws conversionOverflow ArithmeticException because to large
//'' + reallyHuge.setScale(0, RoundingMode.DOWN).intValueExact() == reallyHugeAsBigIntString
and: 'if using Java 8, BigInteger has intValueExact() just like BigDecimal'
//decimal.toBigInteger().intValueExact() == decimal.setScale(0, RoundingMode.DOWN).intValueExact()
}
Bem, você poderia ligar BigDecimal.intValue()
:
Converte este BigDecimal em um int.Esta conversão é análoga a uma conversão primitiva estreita de double para short conforme definido na especificação da linguagem Java:qualquer parte fracionária deste BigDecimal será descartada e, se o "BigInteger" resultante for muito grande para caber em um int, apenas os 32 bits de ordem inferior serão retornados.Observe que esta conversão pode perder informações sobre a magnitude geral e a precisão deste valor BigDecimal, bem como retornar um resultado com o sinal oposto.
Você pode então chamar explicitamente Integer.valueOf(int)
ou deixe o boxe automático fazer isso por você se estiver usando uma versão suficientemente recente do Java.
O seguinte deve resolver o problema:
BigDecimal d = new BigDecimal(10);
int i = d.intValue();
Você já tentou ligar BigInteger#intValue() ?
Descobri que o acima não funcionou para mim.Eu estava extraindo um valor de célula de uma JTable, mas não consegui converter para double ou int etc.Minha solução:
Object obj = getTable().getValueAt(row, 0);
onde a linha 0 seria sempre um número.Espero que isso ajude alguém que ainda está navegando!