【问题标题】:BigDecimal adding wrong valueBigDecimal 添加错误的值
【发布时间】:2017-05-12 07:19:22
【问题描述】:

我有一个这样定义的BigDecimal

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

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

0.6999999999999999555910790149937383830547332763671875

这会导致一些错误的计算。有谁知道将0.7 的确切值作为BigDecimal 的方法?把它改成0.71会看到正确的结果,但不应该是这样的

【问题讨论】:

  • Double检查构造函数逻辑,你会明白这种转换之间的困难;)
  • 感谢所有答案,但我真的不明白为什么转换失败。我以为如果位数超过精度,我只会用双精度得到错误的值。 0.7 应该完全是 0.7,double d=0.7d; Float f=new Float(0.7); System.out.println(f); System.out.println(d); 在这两种情况下都正好打印 0.7。
  • double d = 0.7d; 不要只存储像int = 0 dec = 7 这样的值,而是使用使用指数的尾数逻辑,反向逻辑用于创建BigDecimal。在这种情况下会发生浮点值精度丢失
  • 源代码到double的转换不精确。从(不精确的)double 到 BigDecimal 的转换是精确的。
  • @XtremeBaumer:很明显,您并不完全理解 binary 浮点类型,例如 doublefloat。请查看docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html 或者更简单的floating-point-gui.de/formats/fp。这可能解释了为什么 0.7 永远不能以这种格式精确表示,但为什么例如0.50.3750.75 甚至 0.0234375 (= 3/128) 都可以。

标签: java bigdecimal


【解决方案1】:

使用字符串文字:

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

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

【讨论】:

  • 你是对的。我把它弄混了。在分析器中更正。
【解决方案2】:

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

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

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

注意:这通常是将double(或float)转换为BigDecimal的首选方法,因为返回的值等于构造@987654339所产生的值@来自使用Double.toString(double)的结果。

这就是你的答案:使用BigDecimal.valueOf(0.7d),而不是new BigDecimal(0.7d)

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      这里有三种方式:

      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)
      

      【讨论】:

      • sd = BigDecimal.valueOf(0.7d); 怎么样,也是 javadoc 推荐的?
      • 是的,那将是最好的解决方案。谢谢@Andreas
      【解决方案5】:

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

      然而,即使是这样,简单的String.format() 也不够,因为String.format() 对默认的Locale 敏感,并根据系统/VM 设置输出不同的小数分隔符,而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);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-21
        • 2020-03-20
        • 2021-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多