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

Java BigDecimal(double)构造函数的不可预测性,java,Java,我最近在一个项目中开始使用Sonar,我在使用构造函数new BigDecimal(double val)时违反了PMD规则。当我阅读java文档时,我发现新的BigDecimal(double val)有些不可预测,我应该使用可预测的新的BigDecimal(String val) 下面是javadoc对BigDecimal公共BigDecimal(double val)的说明: 将double转换为BigDecimal,即精确的十进制 表示双精度二进制浮点值。天平 返回的BigDecimal

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

下面是javadoc对
BigDecimal
公共BigDecimal(double val)
的说明:

将double转换为BigDecimal,即精确的十进制 表示双精度二进制浮点值。天平 返回的BigDecimal的最小值为(10scale×) val)是一个整数

注:

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

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

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


为什么这个构造函数真的存在?新的BigDecimal(stringval)对于这个问题还不够吗?什么时候我应该使用新的BigDecimal(double val)构造函数?

因为如果您已经开始使用一个
double
值作为数据,那么您已经失去了精度。因此,没有它将迫使您将其转换为
String
for
bigdecimic
以将其转换回


而且,有时您可能只需要这个值。

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

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

最好是没有

如果您愿意使用大小数,使用带双精度的构造函数意味着失去精确数字表示的所有好处

为什么这个构造函数真的存在?

因为有时候你只有double,你想转换成BigDecimal。强迫您在两者之间使用
.toString()
将是愚蠢的;)

为什么这个构造函数真的存在

它将实际表示的
double
值转换为一个BigDecimal。BigDecimal的全部目的是提供尽可能多的精度,这就是这个构造函数所做的

如果您想获取通过少量四舍五入得到的值,可以使用
Double.toString(Double)
uses

System.out.println(BigDecimal.valueOf(0.1));
印刷品

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

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

当您使用
double
时,应始终应用合理的舍入。但是,如果你这样做了,你可能会发现你不需要大十进制

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


但是,如果从文件中读取二进制双精度值,则构造函数
BigDecimal(double val)
非常有用。很明显,在几乎所有情况下,您都希望使用与“double”完全相同的大小数。

不可预测?我不同意

final double before = 0.1;
BigDecimal bd = new BigDecimal(before);
double after = bd.doubleValue();
assert before == after;
理解的关键:

assert 0.1 == 0.1000000000000000055511151231257827021181583404541015625;
如果您真正的意思是“0.1”,那么是的,使用字符串构造函数=),但是如果您已经有了一个
double
,那么这个double就不是“0.1”

你引用的JavaDoc说:

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

我不明白确切的转换是如何“不可预测”的,我非常赞成从这段引文中删除“必须”一词


在一天结束时,两个构造器都像预期的那样工作。如果有字符串“0.1”,则BigDecimal(字符串)会精确转换此值。如果你有一个双精度0.1,那么BigDecimal(double)会准确地转换这个值。

那么,如果我使用的是双精度值,然后转换成字符串,然后转换成BigDecimal,我会失去精度吗?例如,使用Double.toString?可以,因为Double.toString()是一种狡猾的野兽。它实际上并没有返回传递给它的双精度的确切值。相反,它返回最短的字符串,在解析该字符串时,该字符串仍将转换为指定的双精度值。这有点让人困惑,但基本上,这意味着如果Double.toString(0.10000000000000055115123)是“0.1”,则可以保证Double.parseDouble(“0.1”)也是0.10000000000000055115123。这是一个棘手的问题,为什么会丢失精度,但在控制台上打印双精度值时,它看起来从来都不像。之所以会丢失精度,是因为新的BigDecimal(double.toString(0.1d)),实际上表示精确的“0.1”值,即使0.1d是
assert 0.1 == 0.1000000000000000055511151231257827021181583404541015625;