javascript - JavaScript 中的大数

标签 javascript floating-point bigdecimal

我是 JavaScript 的新手(我有 Java 背景),我正在尝试用少量资金进行一些财务计算。

我最初的做法是:

<script type="text/javascript">
    var normBase = ("[price]").replace("$", "");
    var salesBase = ("[saleprice]").replace("$", "");
    var base;
    if (salesBase != 0) {
        base = salesBase;
    } else {
        base = normBase;
    }
    var per5  = (base - (base * 0.05));
    var per7  = (base - (base * 0.07));
    var per10 = (base - (base * 0.10));
    var per15 = (base - (base * 0.15));
    document.write
        (
        '5% Off: $'  + (Math.ceil(per5  * 100) / 100).toFixed(2) + '<br/>' +
        '7% Off: $'  + (Math.ceil(per7  * 100) / 100).toFixed(2) + '<br/>' +
        '10% Off: $' + (Math.ceil(per10 * 100) / 100).toFixed(2) + '<br/>' +
        '15% Off: $' + (Math.ceil(per15 * 100) / 100).toFixed(2) + '<br/>'
    );
</script>

除了它总是四舍五入外,它运行良好 (Math.ceil)。 Math.floor 也有同样的问题,Math.round 也不适合 float 。

在 Java 中,我会从一开始就完全避免使用 float ,但是在 JavaScript 中似乎没有默认包含类似的东西。

问题是,所有提到的库要么已损坏,要么用于不同的目的。 jsfromhell.com/classes/bignumber 库非常接近我的需要,但是我在舍入和精度方面遇到了奇怪的问题......无论我将 Round Type 设置为什么,它似乎自己决定。因此,例如,精度为 2 且圆类型为 ROUND_HALF_UP 的 3.7107 在本应为 3.71 时以某种方式结束为 3.72。

我还尝试了@JasonSmith BigDecimal 库(来自 Java 的 BigDecimal 的加工端口),但它似乎适用于我没有运行选项的 node.js。

我如何使用 vanilla JavaScript(并且可靠)来完成此操作,或者是否有一个现代的(上面提到的那些现在已经有很多年了)我可以使用并且得到维护并且没有损坏?

最佳答案

由于我们原生支持 BigInt,因此不再需要太多代码来实现 BigDecimal

这是一个基于 BigIntBigDecimal 类,具有以下特征:

  • 小数位数配置为常量,适用于所有实例。
  • 是否 chop 或舍入过多的数字配置为 bool 常量。
  • 实例将十进制数存储为 BigInt,乘以 10 的幂以包含小数。
  • 所有计算都使用那些 BigInt 值进行。
  • 传递给addsubtractmultiplydivide 的参数可以是数字、字符串或实例的 BigDecimal
  • 这些方法返回新实例,因此 BigDecimal 被视为不可变的。
  • toString 方法重新引入小数点。
  • BigDecimal 可以强制转换为数字(通过隐式调用 toString),但这显然会导致精度损失。

class BigDecimal {
    // Configuration: constants
    static DECIMALS = 18; // number of decimals on all instances
    static ROUNDED = true; // numbers are truncated (false) or rounded (true)
    static SHIFT = BigInt("1" + "0".repeat(BigDecimal.DECIMALS)); // derived constant
    constructor(value) {
        if (value instanceof BigDecimal) return value;
        let [ints, decis] = String(value).split(".").concat("");
        this._n = BigInt(ints + decis.padEnd(BigDecimal.DECIMALS, "0")
                                     .slice(0, BigDecimal.DECIMALS)) 
                  + BigInt(BigDecimal.ROUNDED && decis[BigDecimal.DECIMALS] >= "5");
    }
    static fromBigInt(bigint) {
        return Object.assign(Object.create(BigDecimal.prototype), { _n: bigint });
    }
    add(num) {
        return BigDecimal.fromBigInt(this._n + new BigDecimal(num)._n);
    }
    subtract(num) {
        return BigDecimal.fromBigInt(this._n - new BigDecimal(num)._n);
    }
    static _divRound(dividend, divisor) {
        return BigDecimal.fromBigInt(dividend / divisor 
            + (BigDecimal.ROUNDED ? dividend  * 2n / divisor % 2n : 0n));
    }
    multiply(num) {
        return BigDecimal._divRound(this._n * new BigDecimal(num)._n, BigDecimal.SHIFT);
    }
    divide(num) {
        return BigDecimal._divRound(this._n * BigDecimal.SHIFT, new BigDecimal(num)._n);
    }
    toString() {
        const s = this._n.toString().padStart(BigDecimal.DECIMALS+1, "0");
        return s.slice(0, -BigDecimal.DECIMALS) + "." + s.slice(-BigDecimal.DECIMALS)
                .replace(/\.?0+$/, "");
    }
}

// Demo
var a = new BigDecimal("123456789123456789876");
var b = a.divide("10000000000000000000");
var c = b.add("9.000000000000000004");
console.log(b.toString());
console.log(c.toString());
console.log(+c); // loss of precision when converting to number

关于javascript - JavaScript 中的大数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16742578/

相关文章:

javascript - 标题中的 R Shiny 动态文本(响应式(Reactive) renderUI)

javascript - 如何使用 JQuery 将动态小数添加到 div 中的另一个小数

java - 大十进制舍入

java - 删除尾随数字

c - 错误: invalid operands to binary >> (have 'float' and 'int' )

java - 如何使用 groupBy 创建一个值为 BigDecimal 字段平均值的 map ?

javascript - BackboneJS : Avoid calling _. 绑定(bind)()

javascript - 为什么我的 javascript/jquery 代码不能按预期用于复选框

java - 如何在Java中建立float和int值之间的双向一对一映射?

c++ - pow(num,2) 的效率?