将BigDecimal转换为整数

145

我有一个返回BigDecimal的Hibernate方法。

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

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

是否有解决办法?


6
我改正了您的标题。这是“转化”,而不是“铸造”。 - user207421
8个回答

231

1
intValueExact()是一个很好的建议;它在1.5中被添加。 - Anon
调用 intValue() 是危险的,除非你百分之百确定该数字在 Integer 范围内。 - Snekse
1
这个有意义吗:"Integer.valueOf(myBigDecimal.intValueExact())"? - OddDev

34

是的。我猜它不会包含比Integer.MAX_VALUE更大的值。 - Vishal
2
除了获取对象而不是原始值之外,没有必要执行“valueOf”操作,对吗? - Basil
我认为这应该是官方答案,因为a.提及了限制,b.可以防止自动装箱。 - ledlogic

27

简而言之:

使用以下任一工具进行通用转换需求

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

推理

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

  • BigDecimal 可能有非零小数部分吗?
  • BigDecimal 可能不适合 Integer 范围吗?
  • 您是否希望将非零小数部分四舍五入或截断?
  • 您希望如何四舍五入非零小数部分?

如果您对前两个问题的回答是否,则可以像其他人建议的那样使用 BigDecimal.intValueExact(),让它在发生意外情况时崩溃。

如果您对问题2不绝对有 100%的把握,那么intValue()永远都是错误的答案。

改进

让我们基于其他答案使用以下假设。

  • 我们可以失去精度并截断值,因为这就是 intValueExact() 和自动装箱所做的
  • BigDecimal 大于 Integer 范围时,我们希望抛出异常,因为除非您有非常特定的需要,否则任何其他情况都会很疯狂,因为您删除高阶位时发生了环绕。

在这些参数下,如果我们的小数部分非零,intValueExact() 会在不希望它抛出异常时抛出异常。另一方面,如果我们的 BigDecimal 太大,则 intValue() 不会在应该抛出异常时抛出异常。

要同时获得两全其美的效果,先四舍五入 BigDecimal,然后再进行转换。这也有助于更好地控制舍入过程。

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

20

你可以调用 BigDecimal.intValue() 方法:

将此 BigDecimal 对象转换为 int。此转换类似于 Java 语言规范中定义的从 double 到 short 的缩小原始转换:此 BigDecimal 的任何小数部分都将被舍弃,如果生成的“BigInteger”太大而无法适应 int,则仅返回低位32位。请注意,此转换可能会丢失有关此 BigDecimal 值的总体大小和精度的信息,并返回相反符号的结果。

然后你可以显式调用 Integer.valueOf(int) 或者让自动装箱在使用足够新的 Java 版本时自动完成。


9
以下内容应该能解决问题:
BigDecimal d = new BigDecimal(10);
int i = d.intValue();

1


-4

我发现上述方法对我不起作用。我正在从JTable中提取单元格值,但无法转换为double或int等。我的解决方案:

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

第0行始终是一个数字。希望这能帮助那些仍在滚动的人!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接