Преобразование BigDecimal в Integer - PullRequest
125 голосов
/ 28 октября 2010

У меня есть метод Hibernate, который возвращает мне BigDecimal.У меня есть другой метод API, которому нужно передать это число, но он принимает Integer в качестве параметра.Я не могу изменить типы возврата или типы переменных обоих методов.

Теперь, как преобразовать BigDecimal в Integer и передать его во второй метод?

Есть ли выход из этого?

Ответы [ 8 ]

196 голосов
/ 28 октября 2010

Вы бы позвонили myBigDecimal.intValueExact() (или просто intValue()), и он даже выдаст исключение, если вы потеряете информацию. Это возвращает int, но об этом позаботится autoboxing.

32 голосов
/ 28 октября 2010

Можете ли вы гарантировать, что BigDecimal никогда не будет содержать значение больше Integer.MAX_VALUE?

Если да, то ваш код называется intValue:

Integer.valueOf(bdValue.intValue())
18 голосов
/ 11 марта 2015

TL; DR

Используйте один из них для универсальных конверсионных нужд

//Java 7 or below
bigDecimal.setScale(0, RoundingMode.DOWN).intValueExact()
//Java 8    
bigDecimal.toBigInteger().intValueExact()

Рассуждения

Ответ зависит от требований и от того, как вы ответите на этот вопрос.

  • Будет ли BigDecimal потенциально иметь ненулевую дробную часть?
  • Неужели BigDecimal потенциально не вписывается в диапазон Integer?
  • Хотели бы вы, чтобы ненулевые дробные части были округлены или усечены?
  • Как бы вы хотели округлить ненулевые дробные части?

Если вы ответили «нет» на первые два вопроса, вы можете просто использовать BigDecimal.intValueExact(), как предлагали другие, и позволить ему взорваться, когда произойдет что-то неожиданное.

Если вы не уверены на 100% в вопросе № 2, тогда intValue() - это всегда неправильный ответ.

Делаем лучше

Давайте использовать следующие предположения на основе других ответов.

  • Мы в порядке с потерей точности и усечением значения, потому что это то, что intValueExact() и автобокс делают
  • Мы хотим создать исключение, когда BigDecimal больше, чем диапазон Integer, потому что все остальное было бы сумасшествием, если у вас нет особой необходимости в переборе, которое происходит, когда вы отбрасываете старшие биты.

Учитывая эти параметры, intValueExact() выдает исключение, когда мы не хотим, чтобы, если наша дробная часть отлична от нуля. С другой стороны, intValue() не выдает исключение, когда должно быть, если наш BigDecimal слишком велик.

Чтобы получить лучшее из обоих миров, сначала округлите BigDecimal, затем конвертируйте. Это также дает вам больше контроля над процессом округления.

Испытание Спока 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()
}
17 голосов
/ 28 октября 2010

Ну, вы могли бы позвонить BigDecimal.intValue():

Преобразует этот BigDecimal в int.Это преобразование аналогично сужающему примитивному преобразованию из двойного в короткое, как определено в Спецификации языка Java: любая дробная часть этого BigDecimal будет отброшена, и если результирующий «BigInteger» будет слишком большим, чтобы поместиться в int, только младшее-заказ 32 бита возвращается.Обратите внимание, что это преобразование может потерять информацию об общей величине и точности этого значения BigDecimal, а также вернуть результат с обратным знаком.

Затем можно явно вызвать Integer.valueOf(int) или разрешить автобокс сделает это за вас, если вы используете достаточно свежую версию Java.

8 голосов
/ 28 октября 2010

Следующие должны сделать трюк:

BigDecimal d = new BigDecimal(10);
int i = d.intValue();
1 голос
/ 28 октября 2010

Вы пытались позвонить BigInteger # intValue () ?

0 голосов
/ 30 апреля 2016

Я обнаружил, что вышеупомянутое не работает для меня.Я извлекал значение ячейки из JTable, но не мог привести к удвоению, int и т. Д. Мое решение:

Object obj = getTable().getValueAt(row, 0);

где строка 0 всегда будет числом.Надеюсь, это поможет любому, кто все еще пользуется прокруткой!

0 голосов
/ 28 октября 2010
...