如何知道 BigDecimal 是否可以准确地转换为 float 或 double?
How to know if a BigDecimal can exactly convert to float or double?
Class BigDecimal
有一些有用的方法来保证无损转换:
byteValueExact()
shortValueExact()
intValueExact()
longValueExact()
但是,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_INFINITY
或 Double.NEGATIVE_INFINITY
用于双精度和 Float.POSITIVE_INFINITY
或 Float.NEGATIVE_INFINITY
用于浮点数。
因此,对于 float 和 double 的 exact
方法最合理(也是最简单)的实现,应该简单地检查转换是 returns 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));
}
上面compareTo
instead of equals
的使用是有意为之,以免检查过于严格。 equals
仅当两个 BigDecimal
对象具有相同的值和小数位数(小数部分的大小)时才计算为真,而 compareTo
在不相同时将忽略此差异事情。例如 2.0
与 2.00
.
Class BigDecimal
有一些有用的方法来保证无损转换:
byteValueExact()
shortValueExact()
intValueExact()
longValueExact()
但是,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_INFINITY
或 Double.NEGATIVE_INFINITY
用于双精度和 Float.POSITIVE_INFINITY
或 Float.NEGATIVE_INFINITY
用于浮点数。
因此,对于 float 和 double 的 exact
方法最合理(也是最简单)的实现,应该简单地检查转换是 returns 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));
}
上面compareTo
instead of equals
的使用是有意为之,以免检查过于严格。 equals
仅当两个 BigDecimal
对象具有相同的值和小数位数(小数部分的大小)时才计算为真,而 compareTo
在不相同时将忽略此差异事情。例如 2.0
与 2.00
.