new BigDecimal(double)与new BigDecimal(String)的区别

7

当使用double类型的输入和String类型的输入时,使用BigDecimal会出现不同的结果。

BigDecimal a = new BigDecimal(0.333333333);
BigDecimal b = new BigDecimal(0.666666666);

BigDecimal c = new BigDecimal("0.333333333");
BigDecimal d = new BigDecimal("0.666666666");

BigDecimal x = a.multiply(b);
BigDecimal y = c.multiply(d);

System.out.println(x);
System.out.println(y);

x输出为

0.222222221777777790569747304508155316795087227497352441864147715340493949298661391367204487323760986328125

当y为

0.222222221777777778

我说这是因为双重精度,这样说错了吗?但既然这是一个 BigDecimal ,它不应该是相同的吗?


看一下a、b和c、d... 这已经有所不同了 ;) 可以看一下https://dev59.com/Fmct5IYBdhLWcg3wCZMT。 - pL4Gu33
1
这是因为double的精度有限;这就是IEEE 754。 - fge
1
你可以使用.setScale(10, BigDecimal.ROUND_HALF_UP);进行修正。 - SnakeDoc
4个回答

13

我说这是因为双精度不准确,这样说错了吗?

您绝对是正确的,这正是由于double的不准确性所致。

但既然这是一个BigDecimal,它不应该是相同的吗?

不,它不应该。误差是在您创建new BigDecimal(0.333333333)时引入的,因为0.333333333常量已经内置了一个误差。此时,您无法修复此表示误差:谚语“马儿出门,关门晚了”就是这个意思,所以现在关闭大门已经太迟了。

另一方面,当您传递一个String时,十进制表示与字符串完全匹配,因此您会得到不同的结果。


6
是的,这是浮点数误差。问题在于文字“0.333333333”和“0.666666666”在传递给BigDecimal之前会被表示为双精度浮点数 ---值得注意的是,BigDecimal的构造函数需要一个double作为参数。
这是标准支持的,标准规定浮点字面值默认为双精度浮点数,除非另有规定

这不是浮点数“错误”。浮点数代码的行为完全符合预期。唯一的问题在于程序员对其行为的期望。 “问题”在于 0.333333333 不代表与 new BigDecimal("0.333333333") 相同的有理数。但这不是一个错误,也不能通过除了更改Java语言规范以外的方式进行“修复”,使 0.333333333 不再被解释为IEEE二进制双精度数。 - Solomon Slow
@jameslarge 我知道IEEE二进制双精度浮点数的工作原理---使用它们所导致的不精确性通常被称为“误差”。例如,请参见wikipedia,或Oracle文档。这是指统计误差的意义,而不是FileNotFoundError - Patrick Collins
哦,没错……是那种错误。我猜我最近有点过于专注于软件缺陷了(请不要问我为什么),甚至开始在没有缺陷的地方看到它们。 - Solomon Slow

5
Java文档已经给出了答案。根据Java中BigDecimal(double val)的文档:
这个构造函数的结果可能有些不可预测。有人可能会认为在Java中编写new BigDecimal(0.1)会创建一个BigDecimal,该BigDecimal恰好等于0.1(无标度值为1,标度为1),但实际上它等于0.1000000000000000055511151231257827021181583404541015625。这是因为0.1不能精确地表示为double类型。

好的,这很有道理。谢谢! - dardeshna

0

当你以任何方式定义一个双精度变量时,大多数情况下它不会是你定义的值,而是最接近的二进制表示。你正在将一个双精度数传递给构造函数,因此已经提供了这种小的不精确性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接