TL; DR
Sử dụng một trong số này cho các nhu cầu chuyển đổi phổ quát
//Java 7 or below
bigDecimal.setScale(0, RoundingMode.DOWN).intValueExact()
//Java 8
bigDecimal.toBigInteger().intValueExact()
Lý luận
Câu trả lời phụ thuộc vào yêu cầu là gì và cách bạn trả lời những câu hỏi này.
- Liệu
BigDecimal
có khả năng sẽ có một phần phân số khác không?
- Liệu
BigDecimal
có khả năng không phù hợp với Integer
phạm vi?
- Bạn có muốn các phần phân số khác không được làm tròn hoặc cắt ngắn?
- Làm thế nào bạn muốn phần không phân số tròn?
Nếu bạn trả lời không cho 2 câu hỏi đầu tiên, bạn chỉ có thể sử dụng BigDecimal.intValueExact()
như những người khác đã đề xuất và để nó nổ tung khi có điều gì đó bất ngờ xảy ra.
Nếu bạn không hoàn toàn 100% tự tin về câu hỏi số 2, sau đó intValue()
là luôn luôn là câu trả lời sai.
Làm cho nó tốt hơn
Hãy sử dụng các giả định sau dựa trên các câu trả lời khác.
- Chúng tôi ổn với việc mất độ chính xác và cắt giảm giá trị bởi vì đó là những gì
intValueExact()
và quyền anh tự động làm
- Chúng tôi muốn có một ngoại lệ được ném khi phạm vi
BigDecimal
lớn hơn Integer
phạm vi bởi vì mọi thứ khác sẽ trở nên điên rồ trừ khi bạn có nhu cầu rất cụ thể về sự bao bọc xảy ra khi bạn thả các bit thứ tự cao.
Với các thông số đó, intValueExact()
sẽ đưa ra một ngoại lệ khi chúng ta không muốn nó nếu phần phân số của chúng ta khác không. Mặt khác, intValue()
đừng ném ngoại lệ khi nó BigDecimal
quá lớn nếu chúng ta quá lớn.
Để có được điều tốt nhất của cả hai thế giới, hãy làm tròn BigDecimal
đầu tiên, sau đó chuyển đổi. Điều này cũng có lợi ích là cho bạn quyền kiểm soát nhiều hơn trong quá trình làm tròn.
Kiểm tra Groovy Spock
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()
}