如何知道BigDecimal是否可以完全转换为float或double?


10

BigDecimal有一些有用的方法来保证无损转换:

  • byteValueExact()
  • shortValueExact()
  • intValueExact()
  • longValueExact()

但是,方法floatValueExact()doubleValueExact()不存在。

我阅读了有关方法floatValue()和的OpenJDK源代码doubleValue()。两者似乎分别回退到Float.parseFloat()Double.parseDouble(),它们可能返回正无穷大。例如,解析10,000个9s的字符串将返回正无穷大。据我了解,BigDecimal没有内部无限的概念。此外,解析100路787-9的字符串作为double给出1.0E100,这是不无穷大,但失去精度。

什么是一个合理的实现floatValueExact()doubleValueExact()

我想到了一个double解决方案相结合BigDecimal.doubleValue()BigDecial.toString()Double.parseDouble(String)Double.toString(double),但它看起来凌乱。我想问一下这里,因为可能(必须!)是一个更简单的解决方案。

需要明确的是,我不需要高性能的解决方案。


7
我猜您可以将其转换为double,然后将double转换回BigDecimal,然后查看是否获得相同的值。虽然不确定用例是什么。如果使用双精度数,则您已经接受具有非精确值。如果您需要确切的值,请继续使用BigDecimal。
JB Nizet

Answers:


6

阅读 文档,它都与不numTypeValueExact变量是检查小数部分的存在,或者如果该值过大,数值类型和抛出异常。

至于floatValue()doubleValue(),过类似的检查正在做,但而不是抛出一个异常,而是返回Double.POSITIVE_INFINITYDouble.NEGATIVE_INFINITY双打和Float.POSITIVE_INFINITYFloat.NEGATIVE_INFINITY为浮动。

因此,exact对于float和double方法,最合理(最简单)的实现应仅检查转换是否返回POSITIVE_INFINITYNEGATIVE_INFINITY


此外,请记住,BigDecimal控件是为解决因使用floatdouble由于较大的非理性而导致的精度不足而设计的,因此,正如@JB Nizet 所说,您可以在上面添加的另一项检查是将double或转换为floatBigDecimal以查看是否仍然得到相同的值。这应该证明转换是正确的。

这是这种方法的外观floatValueExact()

public static float floatValueExact(BigDecimal decimal) {
    float result = decimal.floatValue();
    if (!Float.isInfinite(result)) {
        if (new BigDecimal(String.valueOf(result)).compareTo(decimal) == 0) {
            return result;
        }
    }
    throw new ArithmeticException(String.format("%s: Cannot be represented as float", decimal));
}

故意使用compareTo而不是equals上述内容,以免检查变得过于严格。equals仅当两个BigDecimal对象具有相同的值和小数位数(小数的小数部分的大小)时,结果才为true ,而compareTo当这无关紧要时将忽略此差异。例如2.0VS 2.00


很狡猾。正是我需要的!注意:您可能还希望使用Float.isFinite()Float.isInfinite(),但这是可选的。:)
kevinarpe

3
您还应该首选compareTo而不是equals,因为BigDecimal中的equals()会将2.0和2.00视为不同的值。
JB Nizet

无法精确表示为float的值将不起作用。范例:123.456f。我猜这是由于32位浮点数和64位double的有效位(尾数)大小不同。在你上面的代码中,我得到更好的结果:if (new BigDecimal(result, MathContext.DECIMAL32).equals(decimal)) {。这是合理的更改吗?还是我错过了浮点值的另一种极端情况?
kevinarpe

1
@kevinarpe- 123.456f在概念上与您的1.0E100示例相同。令我惊讶的是,如果您需要检查BigDecimal->二进制浮点数的转换是否正确,那么就是问题所在。即不应考虑转换。
Stephen C
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.