如何知道 BigDecimal 是否可以准确转换为浮点数或双精度数?

kev*_*rpe 11 java bigdecimal

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

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

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

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

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

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

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

sma*_*c89 8

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

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

因此,最合理(也是最简单)的exactfloat 和 double 方法实现应该简单地检查转换是否返回POSITIVE_INFINITYNEGATIVE_INFINITY


此外,请记住,BigDecimal它旨在处理因使用floatdouble用于大的无理数而导致的精度不足,因此正如@JB Nizet 评论的那样,您可以添加到上面的另一个检查是将double或转换floatBigDecimal以查看您是否仍然得到相同的值。这应该证明转换是正确的。

这是这样的方法的样子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));
}
Run Code Online (Sandbox Code Playgroud)

使用compareTo而不是equals上面是有意的,以免对检查变得过于严格。equals仅当两个BigDecimal对象具有相同的值和比例(小数部分的小数部分的大小)时才会评估为真,而compareTo在无关紧要时会忽略这种差异。例如2.0vs 2.00.

  • 您还应该更喜欢使用compareTo而不是equals,因为BigDecimal中的equals()会将2.0和2.00视为不同的值。 (3认同)