将BigDecimal转换为Integer

问题

我有Hibernate方法,它返回一个BigDecimal。我有另一个API方法,我需要传递该数字但它接受Integer作为参数。我无法更改两种方法的返回类型或变量类型。

现在如何将BigDecimal转换为Integer并将其传递给第二个方法?

有没有办法解决这个问题?


#1 热门回答(175 赞)

你会打电话给myBigDecimal.intValueExact()(或者只是intValue()),如果丢失信息,它甚至会抛出异常。返回一个int,但autoboxing负责处理。


#2 热门回答(31 赞)

你能保证BigDecimal永远不会包含大于Integer.MAX_VALUE的值吗?

如果是,那么这是你的代码,电话intValue

Integer.valueOf(bdValue.intValue())

#3 热门回答(16 赞)

TL; DR
使用其中一种来满足通用转换需求

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

推理
答案取决于要求是什么以及如何回答这些问题。

  • BigDecimal是否可能具有非零小数部分?
  • BigDecimal可能不适合整数范围吗?
  • 你希望非零小数部分舍入或截断吗?
  • 你希望非零小数部分四舍五入吗?

如果你对前两个问题的回答为"否",则可以使用其他人建议的BigDecimal.intValueExact(),并在发生意外情况时让它爆炸。

如果你对问题2没有绝对100%的自信,那么intValue()就是错误的答案。
让它变得更好
让我们根据其他答案使用以下假设。

  • 我们可以丢失精度并截断值,因为这就是intValueExact()和自动装箱做的
  • 当BigDecimal大于Integer范围时,我们希望抛出一个异常,因为除非你有一个非常特殊的需要,当你丢弃高位时发生这种情况。

鉴于这些参数,如果我们的小数部分非零,我们不希望它出现异常intValueExact()。另一方面,intValue()如果我们的4664878768太大则不会抛出异常。

为了获得两全其美,首先完成BigDecimal,然后转换。这样做的好处是可以让你更好地控制舍入过程。
Spock Groovy Test

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()
}