java BigDecimal子句失败

use*_*499 7 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);
Run Code Online (Sandbox Code Playgroud)

结果:

double result: 0.0
float result: 0.0
BigDecimal result: 0E-59
Run Code Online (Sandbox Code Playgroud)

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

Oli*_*rth 11

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


答案是"0E-59" 等于0.回想一下,一个BigDecimal是未测量的值和小数比例因子的组合:

System.out.println(b1.unscaledValue());
System.out.println(b1.scale());
Run Code Online (Sandbox Code Playgroud)

显示:

0
59
Run Code Online (Sandbox Code Playgroud)

非标度值 0,如所预期."奇怪的"比例值只是0.01的非精确浮点表示的十进制扩展的人为因素:

System.out.println(b2.unscaledValue());
System.out.println(b2.scale());
Run Code Online (Sandbox Code Playgroud)

显示:

1000000000000000020816681711721685132943093776702880859375
59
Run Code Online (Sandbox Code Playgroud)

下一个显而易见的问题是,为什么不方便BigDecimal.toString显示b1为" 0"?答案是字符串表示需要明确.来自JavadoctoString:

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

如果它只是显示" 0",那么你将无法回到这个确切的BigDecimal对象.


小智 7

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

Java精度损失

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


pax*_*blo 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);
    }                          
}
Run Code Online (Sandbox Code Playgroud)

输出两条 equal消息,表明值相同并且您得到并且相减时

您可以尝试将此作为错误提出,然后看看 Oracle 会返回什么结果。他们很可能只是声明0e-59仍然为零,因此不是错误,或者BigDecimal 文档页面上描述的相当复杂的行为正在按预期工作。具体来说,这一点指出:

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

原始值需要可恢复这一事实意味着toString()需要为每个比例生成唯一的字符串,这就是您得到0e-59. 否则,将字符串转换aBigDecimal可能会给出不同的值(未缩放值/缩放元组)。

如果您确实希望无论大小如何,零都显示为“0”,您可以使用类似以下内容的内容:

if (b1.compareTo(BigDecimal.ZERO) == 0) b1 = new BigDecimal(0);
Run Code Online (Sandbox Code Playgroud)