如何知道 BigDecimal 是否可以准确地转换为 float 或 double?

How to know if a BigDecimal can exactly convert to float or double?

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

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

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

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

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

明确地说,我不需要高性能解决方案。

reading the docs 开始,它对 numTypeValueExact 变体所做的只是检查小数部分是否存在,或者如果值对于数字类型来说太大并抛出异常。

至于 floatValue() and doubleValue(),正在进行类似的溢出检查,但不是抛出异常,而是 returns Double.POSITIVE_INFINITYDouble.NEGATIVE_INFINITY 用于双精度和 Float.POSITIVE_INFINITYFloat.NEGATIVE_INFINITY 用于浮点数。

因此,对于 float 和 double 的 exact 方法最合理(也是最简单)的实现,应该简单地检查转换是 returns POSITIVE_INFINITY 还是 NEGATIVE_INFINITY


此外,请记住 BigDecimal 旨在解决因使用 floatdouble 导致的大无理数缺乏精确性的问题,因此作为 @JB Nizet ,您可以添加到上面的另一个检查是将 doublefloat 转换回 BigDecimal 以查看您是否仍然获得相同的值。这应该证明转换是正确的。

对于 floatValueExact():

这样的方法是这样的
public static float floatValueExact(BigDecimal decimal) {
    float result = decimal.floatValue();
    if (!(Float.isNaN(result) || 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 instead of equals的使用是有意为之,以免检查过于严格。 equals 仅当两个 BigDecimal 对象具有相同的值和小数位数(小数部分的大小)时才计算为真,而 compareTo 在不相同时将忽略此差异事情。例如 2.02.00.