TL; DR
Utilice uno de estos para las necesidades de conversión universal.
//Java 7 or below
bigDecimal.setScale(0, RoundingMode.DOWN).intValueExact()
//Java 8
bigDecimal.toBigInteger().intValueExact()
Razonamiento
La respuesta depende de cuáles son los requisitos y cómo responde a estas preguntas.
- ¿Tendrá
BigDecimal
potencialmente una parte fraccionaria distinta de cero?
- ¿
BigDecimal
Potencialmente no encajará en el Integer
rango?
- ¿Desea partes fraccionarias distintas de cero redondeadas o truncadas?
- ¿Cómo le gustaría redondear las partes fraccionarias distintas de cero?
Si respondió no a las 2 primeras preguntas, puede usarlas BigDecimal.intValueExact()
como otros han sugerido y dejar que explote cuando ocurra algo inesperado.
Si no está 100% seguro de la pregunta número 2, entonces siempreintValue()
es la respuesta incorrecta.
Haciéndolo mejor
Usemos los siguientes supuestos basados en las otras respuestas.
- Estamos de acuerdo con perder precisión y truncar el valor porque eso es lo que hacen
intValueExact()
y el auto-boxeo
- Queremos que se produzca una excepción cuando
BigDecimal
sea mayor que el Integer
rango porque cualquier otra cosa sería una locura a menos que tenga una necesidad muy específica de la envoltura que ocurre cuando suelta los bits de alto orden.
Dados esos parámetros, intValueExact()
arroja una excepción cuando no queremos que nuestra parte fraccional sea distinta de cero. Por otro lado, intValue()
no arroja una excepción cuando debería si nuestro BigDecimal
es demasiado grande.
Para obtener lo mejor de ambos mundos, completa el BigDecimal
primero y luego convierte. Esto también tiene el beneficio de darle más control sobre el proceso de redondeo.
Prueba de 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()
}