【问题标题】:How to get the exact value in BigDecimal?如何获得 BigDecimal 中的确切值?
【发布时间】:2021-09-18 02:13:44
【问题描述】:

我想获得具有以下属性的 BigDecimal 值:

  • 舍入模式:半偶数
  • 点后位数:2

我有以下代码:

public BigDecimal standardDeviation() {
            MathContext mc = new MathContext (4,RoundingMode.HALF_EVEN);
            return new BigDecimal(Math.sqrt(variance().doubleValue()), mc);
        }

在我发送一些值的测试中,我遇到了以下失败:

invalid standard deviation ==> expected: <16.73> but was: <16.72>

我该如何解决这个问题?

【问题讨论】:

  • 这里的输入值是多少?这似乎只是选择了错误的舍入模式或错误的期望。请注意,HALF_EVEN 意味着 16.725 将舍入为 16.72 而不是 16.73,即舍入到偶数邻居。您的意思是改用 HALF_UP 吗?
  • 对于几个示例,您可能希望显示舍入前、舍入后的值以及预期值。否则无法建议适当的舍入模式来满足您的要求。在处理浮点值时,术语 exact imo 几乎没有意义。
  • 请注意,95.0095 是完全相同的 ,因此如果您要进行计算,这并不重要。请注意,比较 BigDecimals 不应该使用 equals() 而是使用 compareTo() 因为 equals 会报告 95 和 95.0 是不同的(它们是不同的,但不是从数学的角度来看)。
  • 您将外观与实际值混淆了。它类似于 0 的 int,您希望将其返回为 00 或 000 或 0000。它没有任何区别。这是格式问题。您可以将其作为字符串返回,但必须将其转换回 BigDecimal 才能在计算中使用。但根据值的不同,您可能会失去宝贵的精度,这可能会对这些计算产生不利影响。
  • 也许有人愿意将所有这些优秀的 cmets 汇集到一个答案中?

标签: java floating-point bigdecimal decimal-point mathcontext


【解决方案1】:

对 cme​​ts 的总结:

invalid standard deviation ==&gt; expected: &lt;16.73&gt; but was: &lt;16.72&gt;

请注意,您很可能对此处的舍入模式有误解。

注意 JavaDoc on HALF_EVEN:

“如果舍弃分数左边的数字是奇数,则行为与 RoundingMode.HALF_UP 相同;如果它是偶数,行为与 RoundingMode.HALF_DOWN 相同”

这意味着像 16.725 这样的数字将向下取整而不是向上取整,因此使用 HALF_EVEN 的结果将为 16.72。

如何将 95 (BigDecimal) 转换为 95.00?

从数字的角度来看,95 和 95.00 之间没有区别,因为尾随零与前导零一样微不足道。从这个意义上说,即使 0095.0000 也是相同的数值。

当您处理 BigDecimal 时,您的第一个目标通常是获取计算,从而获得不受“任意”精度问题影响的数值(不支持不确定的精度,但可以将其设置为固定值)并且尽可能准确。

因此,如果您从数字 95、95.0 甚至字符串“95.00”创建 BigDecimal,它们将具有相同的值。

但是,它们不相等,因为数字在内部也有刻度,即 95 和 95.0 通常在内部表示为整数 95 和刻度 0,这意味着不必移动小数点。另一方面,"95.00" 将表示为数字 9500 和 2,这意味着要获得实际数字,您需要将小数点向左移动 2 位。

话虽如此,如果您想检查 BigDecimals 是否相等,请不要使用 equals(),因为这也会考虑比例,但使用 compareTo() == 0,因为它只会比较数值。

我正在开发一个支付系统,即使数字是 95,我也需要返回点后 2 位的数字。

一般来说,这更多是格式问题。只要计算正确完成,您的系统用户并不关心内部有多少小数位。当您需要向用户显示一个数字时,您需要将其转换为字符串,这就是格式化开始的地方。

要生成格式良好的字符串,您可以使用任何基于 java.utilFormatter 的方法,例如String.format(printf("%.2f", bigDecimal);

最后,一种方法可以将 BigDecimal.toString() 的输出实际更改为 95.00,但我不建议将其用于此目的:试试 new BigDecimal(95).setScale(2).toString()

相反,将输出格式化为您需要的小数位数,并使用 setScale() 等方法仅正确舍入,例如如果您想四舍五入到 2 位小数,请尝试 new BigDecimal(16.725).setScale(2, RoundingMode.HALF_UP)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-22
    • 2017-04-25
    • 1970-01-01
    • 2017-04-24
    • 1970-01-01
    相关资源
    最近更新 更多