类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),但它看起来凌乱。我想在这里问,因为可能(必须!)有一个更简单的解决方案。
需要明确的是,我不需要高性能解决方案。
从阅读 的 文档,它都与不numTypeValueExact变量是检查小数部分的存在,或者如果该值过大,数值类型和抛出异常。
至于floatValue()and doubleValue(),正在执行类似的溢出检查,但不是抛出异常,而是返回Double.POSITIVE_INFINITY或Double.NEGATIVE_INFINITY用于双精度和Float.POSITIVE_INFINITY或Float.NEGATIVE_INFINITY用于浮点数。
因此,最合理(也是最简单)的exactfloat 和 double 方法实现应该简单地检查转换是否返回POSITIVE_INFINITY或NEGATIVE_INFINITY。
此外,请记住,BigDecimal它旨在处理因使用float或double用于大的无理数而导致的精度不足,因此正如@JB Nizet 评论的那样,您可以添加到上面的另一个检查是将double或转换float回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));
}
Run Code Online (Sandbox Code Playgroud)
使用compareTo而不是equals上面是有意的,以免对检查变得过于严格。equals仅当两个BigDecimal对象具有相同的值和比例(小数部分的小数部分的大小)时才会评估为真,而compareTo在无关紧要时会忽略这种差异。例如2.0vs 2.00.