【问题标题】:How does rounding work in Number.toFixed function?Number.toFixed 函数中的舍入如何工作?
【发布时间】:2020-04-09 21:25:49
【问题描述】:

我正在用 Java 编写一个 API,代码的行为应该类似于另一个用 Node 编写的微服务。

用Node写的有问题的部分是这样的:

const totalValue =   // this value comes from a query
const risk =         // it is a percentage value from another query

const riskValue = totalValue * (risk / 100);
const available = +(totalValue - riskValue).toFixed(2);

return available;

经过几次测试后,我以这种方式编写了 Java API:

BigDecimal totalValue = ...
BigDecimal risk = ...

BigDecimal riskValue = totalValue.multiply(risk.divide(new BigDecimal(100)));
BigDecimal available = totalValue.subtract(riskValue).setScale(2, RoundingMode.HALF_DOWN));

几天后第一个错误:

totalValue = 62822.65
risk = 10

节点返回:

56540.385.toFixed(2); // "56540.39"

这个具体数字没有像我预期的那样在我的 Java API 中四舍五入。

但是,这个数字是四舍五入的:

56540.395.toFixed(2); // "56540.39"

JavaScriptNumber.toFixed(2)背后的逻辑是什么?

如何在Java中复制这个函数行为?

提前致谢。

【问题讨论】:

标签: javascript java node.js


【解决方案1】:

我不确定我是否完全理解您的问题。如果您的问题是如何将 double 数字四舍五入到 2 小数位,您可以按如下方式进行:

import java.math.BigDecimal;
import java.math.RoundingMode;

public class Main {
    public static void main(String[] args) {
        double n1 = 56540.385;
        BigDecimal rounded1 = BigDecimal.valueOf(n1).setScale(2, RoundingMode.HALF_UP);
        System.out.println(rounded1);

        double n2 = 56540.395;
        BigDecimal rounded2 = BigDecimal.valueOf(n2).setScale(2, RoundingMode.HALF_UP);
        System.out.println(rounded2);
    }
}

输出

56540.39
56540.40

如果您正在寻求其他方面的帮助,请随时发表评论。

【讨论】:

  • 问题是 Node 正在为 56540.395 舍入 HALF_DOWN 并为 56540.385 舍入 HALF_UP。
  • 使用BigDecimal.valueOf,正如我在示例中所展示的那样。我不知道node.js,但这就是它在 Java 中的实现方式。
  • 我会尝试向我的老板解释第一个 API 是错误的,并以某种方式找到 Number.toFixed 函数的替代解决方案。因为,无论是 HALF_DOWN 还是 HALF_UP,它的行为都应该始终相同......我只是无法随机使用一个或另一个来理解它。
  • 我通过在节点中使用BigNumber 解决了这个问题:BigNumber.set({ ROUNDING_MODE: BigNumber.ROUND_HALF_DOWN }); 然后:new BigNumber(56540.385).toFixed(2) 按预期工作
【解决方案2】:

参考 Arvind Kumar Avinash 的答案

问题在于,确切的数字 56540.38556540.395 不存在:浮点数始终基于二进制而不是基于十进制。

证明:

56540.385.toPrecision(18); // returns "56540.3850000000020"
56540.395.toPrecision(18); // returns "56540.3949999999968"

所以表达式NUMBER.toFixed(2);被处理成2个步骤:

  1. NUMBER 被扫描并转换为最接近的可能浮点数。
  2. 对转换后的数字执行toFixed()

如果知道幕后的数学,6540.39 的结果对于这两种情况都是正确的。

一种可能的解决方案是计算所有的美分(因子 100),然后使用 (NUMBER/100).toFixed(2) 进行格式化输出。

【讨论】:

  • 已经 +1... 一些问题:该因子 100 的代码如何?另外,如果我理解正确,“风险”除以 100 也会有基于二进制的问题吗?我现在必须仔细检查我的“固定”代码:risk = new BigNumber(risk).div(100); riskValue = new BigNumber(totalValue).multipliedBy(risk); available = new BigNumber(totalValue).minus(riskValue); BigNumber.set({ ROUNDING_MODE: BigNumber.ROUND_HALF_DOWN }); return +available.toFixed(2);
猜你喜欢
  • 2022-11-17
  • 1970-01-01
  • 2021-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-10
  • 1970-01-01
相关资源
最近更新 更多