16

Java 怎么BigDecimal会这么痛苦?

Double d = 13.3D;

BigDecimal bd1 = new BigDecimal(d);
BigDecimal bd2 = new BigDecimal(String.valueOf(d));


System.out.println("RESULT 1: "+bd1.toString());
System.out.println("RESULT 2: "+bd2.toString());

RESULT 1: 13.300000000000000710542735760100185871124267578125
RESULT 2: 13.3

是否存在需要结果 1 的情况?我知道 Java 1.5 改变了toString()方法,但这是预期的结果吗?

我也意识到BigDecimaldoubleValue()等,但我正在使用的库有用地使用 atoString()并且我无法改变它:-(

干杯。

4

5 回答 5

51

好吧,API确实解决了构造函数中这种明显的不一致BigDecimal(double val)

  1. 此构造函数的结果可能有些不可预测。有人可能会假设用 Java 编写 new BigDecimal(0.1) 会创建一个正好等于 0.1 的 BigDecimal(未缩放的值 1,缩放为 1),但它实际上等于 0.10000000000000000555511151231257827021181583404541015625。这是因为 0.1不能完全表示为双精度数(或者,就此而言,不能表示为任何有限长度的二进制分数)。因此,传递给构造函数的值并不完全等于 0.1,尽管看起来如此。

  2. 另一方面,String 构造函数是完全可预测的:编写 new BigDecimal("0.1") 会创建一个正好等于 0.1 的 BigDecimal,正如人们所期望的那样。因此,一般建议优先使用 String 构造函数。

  3. 当必须将 double 用作 BigDecimal 的源时,请注意此构造函数提供了精确转换;它与使用 Double.toString(double) 方法然后使用 BigDecimal(String) 构造函数将双精度转换为字符串的结果不同。要获得该结果,请使用静态 valueOf(double) 方法

故事的寓意:痛苦似乎是自己造成的,只需使用new BigDecimal(String val)orBigDecimal.valueOf(double val)代替 =)

于 2009-01-20T10:50:54.297 回答
11

您的问题与 无关BigDecimal,与 的所有内容Double都无法准确表示 13.3,因为它在内部使用二进制分数。

所以你的错误是在第一行引入的。第一个BigDecimal只是简单地保留它,同时进行String.valueOf()一些可疑的舍入,导致第二个具有所需的内容,这几乎是靠运气。

于 2009-01-20T10:40:09.390 回答
8

您可能想了解浮点值是如何实现的(IEEE 754-1985)。突然间,一切都会变得晶莹剔透。

于 2009-01-20T10:39:12.993 回答
5

这不是 - 的错BigDecimal- 这是 的错doubleBigDecimal准确地表示 的确切dString.valueOf只显示结果到小数点后几位。

于 2009-01-20T10:38:07.460 回答
3

double用二进制数类型(即, )表示的分数float不能准确地存储在这些类型中。

    Double d = 13.3;        
    BigDecimal bdNotOk = new BigDecimal(d);
    System.out.println("not ok: " + bdNotOk.toString());

    BigDecimal bdNotOk2 = new BigDecimal(13.3);
    System.out.println("not ok2: " + bdNotOk2.toString());

    double x = 13.3;
    BigDecimal ok = BigDecimal.valueOf(x);
    System.out.println("ok: " + ok.toString());

    double y = 13.3;
    // pretty lame, constructor's behavior is different from valueOf static method
    BigDecimal bdNotOk3 = new BigDecimal(y);
    System.out.println("not ok3: " + bdNotOk3.toString());

    BigDecimal ok2 = new BigDecimal("13.3");
    System.out.println("ok2: " + ok2.toString());

    Double e = 0.0;
    for(int i = 0; i < 10; ++i) e = e + 0.1; // some fractions cannot be accurately represented with binary
    System.out.println("not ok4: " + e.toString()); // should be 1


    BigDecimal notOk5 = BigDecimal.valueOf(e);
    System.out.println("not ok5: " + notOk5.toString()); // should be 1

    /* 
     * here are some fractions that can be represented exactly in binary:
     * 0.5   = 0.1   = 1 / 2
     * 0.25  = 0.01  = 1 / 4
     * 0.75  = 0.11  = 3 / 4
     * 0.125 = 0.001 = 1 / 8
     */

输出:

not ok: 13.300000000000000710542735760100185871124267578125
not ok2: 13.300000000000000710542735760100185871124267578125
ok: 13.3
not ok3: 13.300000000000000710542735760100185871124267578125
ok2: 13.3
not ok4: 0.9999999999999999
not ok5: 0.9999999999999999

只需使用BigDecimal.valueOf(d)or new BigDecimal(s)

于 2009-01-20T11:21:14.050 回答