BigDecimal 添加错误的值

BigDecimal adding wrong value

我有一个 BigDecimal 定义如下:

private static final BigDecimal sd = new BigDecimal(0.7d);

如果我打印它,我得到值:

0.6999999999999999555910790149937383830547332763671875

这会导致一些错误的计算。有谁知道如何将 0.7 的精确值设为 BigDecimal?改成0.71会看到正确的结果,但不应该是这样的

使用字符串文字:

private static final BigDecimal sd = new BigDecimal("0.7");

如果您使用 double,实际上会调用 public BigDecimal(double val)。你没有得到 0.7 的原因是它不能用 double 精确表示。有关详细信息,请参阅链接的 JavaDoc。

您应该使用字符串文字中的声明值,例如 new BigDecimal("0.7");

这里有三种方法:

private static final BigDecimal sd = new BigDecimal("0.7");
private static final BigDecimal sd = new BigDecimal(0.7d, MathContext.DECIMAL32);
private static final BigDecimal sd = new BigDecimal(0.7d, MathContext.DECIMAL64)

也许如果您费心阅读文档,即您正在使用的 constructor 的 javadoc,您就已经知道答案了。

  1. When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.

当您查看 BigDecimal.valueOf(double) 的 javadoc 时,您会发现:

Note: This is generally the preferred way to convert a double (or float) into a BigDecimal, as the value returned is equal to that resulting from constructing a BigDecimal from the result of using Double.toString(double).

所以有你的答案:使用BigDecimal.valueOf(0.7d),而不是new BigDecimal(0.7d)

double 构建 BigDecimal 非常复杂。首先,它只能通过绕行字符串来完成。 (你不能得到带有 doubleMathContext 的构造函数。我已经尝试了很多。最近在小数点前的位数需要更改的情况下舍入,它变得困难。因此 Javadoc 中警告你不应该使用它。)

然而,即使在那里,简单的 String.format() 也是不够的,因为 String.format() 对默认值 Locale 敏感,并根据 [=27= 输出不同的小数点分隔符] 设置,而 BigDecimal 构造函数始终需要一个点作为小数点分隔符。所以你必须用 Locale.US 构造你自己的 Formatter。如果你有这个并且运行,你会收到一个未关闭资源的警告。

我发现这个有效:

static BigDecimal toBigDecimal(double value, int decimalPlaces) {
    String format = "%." + decimalPlaces + "f";
    try (Formatter formatter = new Formatter(Locale.US)) {
        String formatted = formatter.format(format, value).toString();
        return new BigDecimal(formatted);
    }
}