从Java BigDecimal转换为double的精度下降
问题内容:
我正在使用完全基于双精度的应用程序,并且在将字符串解析为双精度的一种实用程序方法中遇到麻烦。我找到了一个解决方法,其中使用BigDecimal进行转换可以解决该问题,但是当我打算将BigDecimal转换回double时又提出了另一个问题:我失去了几个精度位。例如:
import java.math.BigDecimal;
import java.text.DecimalFormat;
public class test {
public static void main(String [] args){
String num = "299792.457999999984";
BigDecimal val = new BigDecimal(num);
System.out.println("big decimal: " + val.toString());
DecimalFormat nf = new DecimalFormat("#.0000000000");
System.out.println("double: "+val.doubleValue());
System.out.println("double formatted: "+nf.format(val.doubleValue()));
}
}
这将产生以下输出:
$ java test
big decimal: 299792.457999999984
double: 299792.458
double formatted: 299792.4580000000
格式化的double值表明它已经失去了第三位的精度(应用程序要求较低的精度)。
如何获得BigDecimal来保留这些额外的精度?
谢谢!
赶上这篇文章后更新。有人提到这超出了double数据类型的精度。除非我未正确阅读此参考文献:http
:
//java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2.3,
否则double原语的最大指数值为E max = 2 K-1 -1,并且标准实现为K = 11。因此,最大指数应为511,不是吗?
问题答案:
double
使用该数字,您已达到的最高精度。不能做 在这种情况下,该值将四舍五入。从的转换BigDecimal
是无关的,并且精度问题是相同的。例如:
System.out.println(Double.parseDouble("299792.4579999984"));
System.out.println(Double.parseDouble("299792.45799999984"));
System.out.println(Double.parseDouble("299792.457999999984"));
输出为:
299792.4579999984
299792.45799999987
299792.458
对于这些情况double
,小数点后的精度超过3位。对于您的数字,它们恰好是零,这是您可以放入的最接近的表示形式double
。在这种情况下,它更接近四舍五入,因此您的9似乎消失了。如果您尝试这样做:
System.out.println(Double.parseDouble("299792.457999999924"));
您会注意到它保留了9,因为它接近四舍五入:
299792.4579999999
如果您要求保留数字中的 所有
数字,则必须更改在上操作的代码double
。您可以BigDecimal
代替它们使用。如果您需要性能,那么尽管我不知道有任何可用的库,但您可能想将BCD作为一种选择。
为了响应您的更新:双精度浮点数的最大指数实际上是1023。但这不是您的限制因素。您的数字超出了代表有效位数的52个小数位的精度,请参阅IEEE
754-1985。
使用此浮点转换可以看到您的二进制数字。由于262144(2
^ 18)最接近,因此指数为18。如果您使用小数位然后以二进制形式向上或向下一位,您会发现没有足够的精度来表示您的数字:
299792.457999999900 // 0010010011000100000111010100111111011111001110110101
299792.457999999984 // here's your number that doesn't fit into a double
299792.458000000000 // 0010010011000100000111010100111111011111001110110110
299792.458000000040 // 0010010011000100000111010100111111011111001110110111