丢失从java BigDecimal转换为double的精度 [英] losing precision converting from java BigDecimal to double

查看:2706
本文介绍了丢失从java BigDecimal转换为double的精度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用完全基于双精度的应用程序,并且在一个将字符串解析为双精度的实用方法中遇到问题。我已经找到一个修复,使用BigDecimal进行转换,解决了这个问题,但是当我将BigDecimal转换为双精度时,引发了另一个问题:我失去了几个精度。例如:

  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
双格式:299792.4580000000

格式化的double表示它在第三个位置之后丢失了精度(应用程序需要较低的精度)。 p>

如何让BigDecimal保留这些额外的精确度?



谢谢!






赶上这篇文章后更新。几个人提到这超出了双数据类型的精度。除非我不正确地阅读此参考:
http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2.3
那么双元素的最大指数值为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



使用此浮点数转换查看你的二进制数。指数是18,因为262144(2 ^ 18)是最接近的。如果你采取分数位,并以二进制形式上下移动,你可以看到没有足够的精度表示你的数字:

  299792.457999999900 // 0010010011000100000111010100111111011111001110110101 
299792.457999999984 //这是您的号码不适合双
299792.458000000000 // 0010010011000100000111010100111111011111001110110110
299792.458000000040 // 0010010011000100000111010100111111011111001110110111


I am working with an application that is based entirely on doubles, and am having trouble in one utility method that parses a string into a double. I've found a fix where using BigDecimal for the conversion solves the issue, but raises another problem when I go to convert the BigDecimal back to a double: I'm losing several places of precision. For example:

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()));
    }
}

This produces the following output:

$ java test
big decimal: 299792.457999999984
double: 299792.458
double formatted: 299792.4580000000

The formatted double demonstrates that it's lost the precision after the third place (the application requires those lower places of precision).

How can I get BigDecimal to preserve those additional places of precision?

Thanks!


Update after catching up on this post. Several people mention this is exceeding the precision of the double data type. Unless I'm reading this reference incorrectly: http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2.3 then the double primitive has a maximum exponential value of Emax = 2K-1-1, and the standard implementation has K=11. So, the max exponent should be 511, no?

解决方案

You've reached the maximum precision for a double with that number. It can't be done. The value gets rounded up in this case. The conversion from BigDecimal is unrelated and the precision problem is the same either way. See this for example:

System.out.println(Double.parseDouble("299792.4579999984"));
System.out.println(Double.parseDouble("299792.45799999984"));
System.out.println(Double.parseDouble("299792.457999999984"));

Output is:

299792.4579999984
299792.45799999987
299792.458

For these cases double has more than 3 digits of precision after the decimal point. They just happen to be zeros for your number and that's the closest representation you can fit into a double. It's closer for it to round up in this case, so your 9's seem to disappear. If you try this:

System.out.println(Double.parseDouble("299792.457999999924"));

You'll notice that it keeps your 9's because it was closer to round down:

299792.4579999999

If you require that all of the digits in your number be preserved then you'll have to change your code that operates on double. You could use BigDecimal in place of them. If you need performance then you might want to explore BCD as an option, although I'm not aware of any libraries offhand.


In response to your update: the maximum exponent for a double-precision floating-point number is actually 1023. That's not your limiting factor here though. Your number exceeds the precision of the 52 fractional bits that represent the significand, see IEEE 754-1985.

Use this floating-point conversion to see your number in binary. The exponent is 18 since 262144 (2^18) is nearest. If you take the fractional bits and go up or down one in binary, you can see there's not enough precision to represent your number:

299792.457999999900 // 0010010011000100000111010100111111011111001110110101
299792.457999999984 // here's your number that doesn't fit into a double
299792.458000000000 // 0010010011000100000111010100111111011111001110110110
299792.458000000040 // 0010010011000100000111010100111111011111001110110111

这篇关于丢失从java BigDecimal转换为double的精度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆