BigDecimal(double)构造函数的不可预测性

我最近在一个项目中开始使用Sonar,并且我得到了关于使用构造函数new BigDecimal(double val)的PMD规则。 当我阅读java文档时,我发现新的BigDecimal(double val)有些不可预测,我应该使用可预测的new BigDecimal(String val)

以下是javadoc对BigDecimal public BigDecimal(double val)说法:

将double转换为BigDecimal,它是double的二进制浮点值的精确十进制表示forms。 返回的BigDecimal的比例是最小值,使得(10scale×val)是整数。

笔记:

这个构造函数的结果可能有点不可预测。 有人可能会假设在Java中编写new BigDecimal(0.1)会创建一个BigDecimal ,它正好等于0.1(未缩放值为1,比例为1),但它实际上等于0.1000000000000000055511151231257827021181583404541015625。 这是因为0.1不能精确地表示为double(或者,就此而言,作为任何有限长度的二进制分数)。 因此,传递给构造函数的值并不完全等于0.1,尽管有外观。

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

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

为什么这个构造函数确实存在? 不是new BigDecimal(String val)足够了吗? 我什么时候应该使用new BigDecimal(double val)构造函数?

为什么这个构造函数确实存在?

它将double的实际表示值转换为BigDecimal。 BigDecimal的重点是提供尽可能多的精度,这就是这个构造函数的作用。

如果你想通过少量的舍入来获得你将获得的值Double.toString(double)使用你可以使用

 System.out.println(BigDecimal.valueOf(0.1)); 

版画

 0.1 

我什么时候应该使用新的BigDecimal(双val)构造函数

当你想知道double真正代表的值时。 您可以根据需要应用自己的舍入。

使用double ,应始终应用合理的舍入。 但是,如果你这样做,你可能会发现你不需要BigDecimal。 ;)

因为如果你已经以double值作为数据开始,那么你已经失去了这种精确性。 因此,没有它会强制您将其转换为String for BigDecimal以将其转换回来。

而且,有时候你可能只需要那个价值。

当您要使用的数据已经作为double值存在时,您将使用double构造函数。 在将其转换为BigDecimal之前必须将其转换为字符串在这种情况下将是浪费。

When should I use the new BigDecimal(double val) constructor?

优选 – 无处。

如果你愿意使用BigDecimals,那么使用带有double的构造函数会失去精确数字表示的所有好处。

Why does this constructor really exists?

因为有时你只有两倍,你想要翻译成BigDecimal。 强制你在中间使用.toString()会很愚蠢;)

我想发表评论,但无法想出如何将“代码”放入评论中,所以我回答:

double val = 0.1; // this is not 0.1, but an approximination as pointed out in other answers. BigDecimal biggy = new BigDecimal(val); // this variable conatains the "same" value as val does, however it is not 0.1, but as pointed out in other answers: 0.100000000000000005551115123

因此,在此处列出的情况下,您可以完全控制源代码,您应该将val写为’String val =“0.1”’

但是,如果从文件中读取二进制双精度数,则构造函数BigDecimal(double val)非常有用。 显然你几乎在所有情况下都会想要与’双打’完全相同的BigDecimals

不可预知的? 我不同意。

 final double before = 0.1; BigDecimal bd = new BigDecimal(before); double after = bd.doubleValue(); assert before == after; 

理解的关键:

 assert 0.1 == 0.1000000000000000055511151231257827021181583404541015625; 

如果你的确意味着“0.1”,那么是的,使用String构造函数=)但是如果你已经有了一个double ,那么那个double不是“0.1”。

你引用的JavaDoc说:

当double必须用作BigDecimal的源时,请注意此构造函数提供了精确的转换;

我没有看到确切的转换是如何“不可预测的”,我非常赞成从这句话中删除“必须”这个词。

在一天结束时,两个构造函数都按预期工作。 如果你有字符串“0.1”,那么BigDecimal(String)会完全转换这个值。 如果你有一个双0.1,那么BigDecimal(double)完全转换这个值。