java BigDecimal子句失败

时间:2013-03-15 09:21:39

标签: java bigdecimal

我尝试了以下代码。但是使用BigDecimal减去时会得到不同的结果。

    double d1 = 0.1;
    double d2 = 0.1;
    System.out.println("double result: "+ (d2-d1));

    float f1 = 0.1F;
    float f2 = 0.1F;
    System.out.println("float result: "+ (f2-f1));

    BigDecimal b1 = new BigDecimal(0.01);
    BigDecimal b2 = new BigDecimal(0.01);

    b1 = b1.subtract(b2);
    System.out.println("BigDecimal result: "+ b1);

结果:

double result: 0.0
float result: 0.0
BigDecimal result: 0E-59

我还在努力。任何人都可以澄清。

8 个答案:

答案 0 :(得分:11)

[这里有很多答案告诉你,二进制浮点不能完全代表0.01,并暗示你所看到的结果在某种程度上是不准确的。虽然第一部分是真的,但这不是真正的核心问题。]


答案是“0E-59” 等于0.回想一下BigDecimal是未缩放值和小数比例因子的组合:

System.out.println(b1.unscaledValue());
System.out.println(b1.scale());

显示:

0
59

未缩放的值 0,如预期的那样。 “奇怪的”比例值只是0.01的非精确浮点表示的十进制扩展的伪像:

System.out.println(b2.unscaledValue());
System.out.println(b2.scale());

显示:

1000000000000000020816681711721685132943093776702880859375
59

下一个显而易见的问题是,为什么BigDecimal.toString只显示b1为“0”,为方便起见?答案是字符串表示需要明确。来自Javadoc for toString

  

可区分的BigDecimal值与此转换的结果之间存在一对一的映射关系。也就是说,每个可区分的BigDecimal值(未缩放的值和比例)在使用toString时都具有唯一的字符串表示形式。如果使用BigDecimal构造函数将该字符串表示转换回BigDecimal(String),则将恢复原始值。

如果它只显示“0”,那么您将无法返回到这个确切的BigDecimal对象。

答案 1 :(得分:7)

使用String中的构造函数:b1 = new BigDecimal("0.01");

Java loss of precision

(幻灯片23) http://strangeloop2010.com/system/talks/presentations/000/014/450/BlochLee-JavaPuzzlers.pdf

答案 2 :(得分:4)

有趣的是,值看起来是相等的,减法确实给你零,它似乎只是打印代码的问题。以下代码:

import java.math.BigDecimal;
public class Test {
    public static void main(String args[]) {
        BigDecimal b1 = new BigDecimal(0.01);
        BigDecimal b2 = new BigDecimal(0.01);
        BigDecimal b3 = new BigDecimal(0);
        if (b1.compareTo(b2) == 0) System.out.println("equal 1");
        b1 = b1.subtract(b2);
        if (b1.compareTo(b3) == 0) System.out.println("equal 2");
        System.out.println("BigDecimal result: "+ b1);
    }                          
}

输出两条 equal条消息,表示值 相同,并且在减去时。< / p>

您可以尝试将其作为一个错误提出来,看看Oracle带来了什么。他们可能只会声明0e-59仍为零,因此不是错误,或者BigDecimal documentation page上描述的相当复杂的行为正如预期的那样工作。具体而言,指出:

  

可区分的BigDecimal值与此转换的结果之间存在一对一的映射关系。也就是说,每个可区分的BigDecimal值(未缩放的值和比例)都具有使用toString的结果的唯一字符串表示形式。如果使用BigDecimal(String)构造函数将该字符串表示形式转换回BigDecimal,则将恢复原始值。

原始值需要可恢复的事实意味着toString()需要为每个比例生成唯一字符串,这就是您获得0e-59的原因。否则,将字符串转换回BigDecimal可能会给您一个不同的值(非标定值/比例元组)。

如果您确实希望零显示为“0”而不管比例如何,您可以使用以下内容:

if (b1.compareTo(BigDecimal.ZERO) == 0) b1 = new BigDecimal(0);

答案 3 :(得分:2)

您必须获得返回值:

BigDecimal b3 = b1.subtract(b2);
System.out.println("BigDecimal result: "+ b3);

答案 4 :(得分:2)

所以真正的问题是:使用以下代码,

BigDecimal b1 = new BigDecimal(0.01);
BigDecimal b2 = new BigDecimal(0.01);
b1 = b1.subtract(b2);

为什么b1.toString()评估为"0E-59"而不评估为"0.0""0E0"或仅"0"


原因是toString()打印BigDecimal规范格式。有关详细信息,请参阅BigDecimal.toString()

最后,0E-59 0.0 - 它是0*10^59,在数学上评估为0.因此,意外结果是内部问题代表BigDecimal

要获取浮点值或双精度值,请使用

b1.floatValue());

b1.doubleValue());

两者都评估为0.0

答案 5 :(得分:2)

BigDecimal(double val)

  

1.此构造函数的结果可能有些不可预测。有人可能会认为在Java中编写新的BigDecimal(0.1)会创建一个   BigDecimal正好等于0.1(未缩放的值为1,带有   比例为1),但它实际上等于   0.1000000000000000055511151231257827021181583404541015625。这是因为0.1不能完全表示为double(或者为此)   重要的是,作为任何有限长度的二进制分数)。因此,价值   传入构造函数的不完全等于   0.1,尽管有外表。

     

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

     

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

答案 6 :(得分:1)

这是一个已知的问题,BigDecimal(double val)API 这个构造函数的结果可能有些不可预测。虽然它在这个插曲中看起来真的很奇怪。实际原因是新的BigDecimal(0.01)产生具有近似值

的BigDecimal
0.01000000000000000020816681711721685132943093776702880859375

具有较长的精度,因此减法的结果也具有较长的精度。

无论如何,我们可以通过这种方式解决“问题”

BigDecimal b1 = new BigDecimal("0.01");
BigDecimal b2 = new BigDecimal("0.01");

或者我们可以使用构造函数来设置精度

BigDecimal b1 = new BigDecimal(0.01, new MathContext(1));
BigDecimal b2 = new BigDecimal(0.01, new MathContext(1));

答案 7 :(得分:1)

像这样使用:

BigDecimal b1 = BigDecimal.valueOf(0.01);
BigDecimal b2 = BigDecimal.valueOf(0.01);

b1 = b1.subtract(b2);
System.out.println("BigDecimal result: "+ b1);