java BigDecimal减法失败

java BigDecimal减法失败,java,bigdecimal,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 Bi

我尝试了以下代码。但使用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

我还在做这个。任何人都可以澄清一下。

有趣的是,这些值似乎相等,减法的结果为零,这似乎只是打印代码的问题。以下代码:

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
消息,指示值相同,并且减法时得到

你可以试着把它当作一个bug,看看甲骨文会带来什么。很可能他们只是说
0e-59
仍然为零,因此不是bug,或者上描述的相当复杂的行为正在按预期工作。具体而言,这一点表明:

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

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

如果您真的希望零显示为“0”,而不考虑比例,则可以使用以下内容:

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

您必须获得返回值:

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

使用字符串中的构造函数:
b1=new BigDecimal(“0.01”)

(幻灯片23)
所以真正的问题是:使用以下代码

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
的规范格式。有关更多信息,请参阅

最后,
0E-59
0.0
——它是
0*10^59
,在数学上计算为0。因此,意外的结果是
BigDecimal
的内部表示

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

b1.floatValue());


两者的计算结果都是
0.0

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

0.01000000000000000020816681711721685132943093776702880859375
它有很高的精度,所以减法的结果也有很高的精度

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

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

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

1.此构造函数的结果可能有些不可预测。有人可能会认为,在Java中编写新的BigDecimal(0.1)会创建 BigDecimal正好等于0.1(未标度值为1,带 比例为1),但实际上等于 0.1000000000000000055511151231257827021181583404541015625. 这是因为0.1不能精确地表示为double(或者 物质,作为任何有限长度的二元分数)。因此,价值 传递给构造函数的不完全等于 0.1,尽管有外观

2.另一方面,字符串构造函数是完全可预测的:编写新的BigDecimal(“0.1”)会创建一个完全可预测的BigDecimal 等于0.1,正如人们所料。因此,一般来说 建议优先使用字符串构造函数 一个

3.当必须将double用作BigDecimal的源时,请注意此构造函数提供了精确的转换;它没有给出正确的答案 与使用 toString(Double)方法,然后使用BigDecimal(字符串) 构造器。要获得该结果,请使用静态值(double) 方法

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


答案是“0E-59”等于0。回想一下,
BigDecimal
是未标度值和十进制比例因子的组合:

System.out.println(b1.unscaledValue());
System.out.println(b1.scale());
显示:

0
59
1000000000000000020816681711721685132943093776702880859375
59
如预期,未标度值0。“奇怪”刻度值只是0.01非精确浮点表示的十进制扩展的产物:

System.out.println(b2.unscaledValue());
System.out.println(b2.scale());
显示:

0
59
1000000000000000020816681711721685132943093776702880859375
59
下一个明显的问题是,为方便起见,
BigDecimal.toString
为什么不将
b1
显示为“
0
”?答案是字符串表示需要明确。从:

可分辨的
BigDecimal
值与此转换的结果之间存在一对一的映射。也就是说,由于使用了
toString
,每个可区分的
BigDecimal
值(未标度的值和刻度)都有一个唯一的字符串表示形式。如果使用
BigDecimal(string)
构造函数将该字符串表示转换回
BigDecimal
,则将恢复原始值

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

像这样使用:

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

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

他正在做那件事没问题!编辑完问题后,您遇到了一个问题,即浮点数的精度有限,这个答案显然无法解决这个问题:)确实如此。但这并不能回答问题;为什么会这样