JS fractions for big numbers with no precision loss [已关闭]

JS fractions for big numbers with no precision lose [closed]

提问人:valerii15298 提问时间:5/27/2023 最后编辑:valerii15298 更新时间:5/27/2023 访问量:224

问:


想改进这个问题吗?更新问题,使其仅通过编辑这篇文章来关注一个问题。

6个月前关闭。

支持分数的大数字:

我想在不损失精度的情况下执行数学运算。 如何使用 JavaScript 做到这一点? 首先,我尝试了 decimal.js,但它不能保持精度,如您在下一个示例中看到的那样:

import {Decimal} from 'decimal.js';
const a1 = new Decimal(1);
const a2 = new Decimal(3);
console.log(a1.div(a2).toFraction()); // should be `1/3` but I get `33333333333333333333/100000000000000000000`

接下来我尝试了 fraction.js,但它不适用于大数字,例如:

import Fraction from 'fraction.js';
const n1 = new Fraction("99999999999999999999999999999999999999999999999");
const n2 = new Fraction("99999999999999999999999999999999999999999999990");
console.log(+(n1.sub(n2))) // should be 9, but I get 0

是否有任何解决方案可以处理相对较大的数字(假设与支持相同)但具有高精度(与支持相同)。decimal.jsfraction.js

我检查了引擎盖下的使用,所以对于大量数字没有任何好处:mathjsfraction.js

import * as math from "mathjs";
const a1 = math.fraction(math.number("99999999999999999999999999999999999999999999999"))
const a2 = math.fraction(math.number("99999999999999999999999999999999999999999999990"))
console.log(+(math.subtract(a1, a2))) // should be 9 but I get 0
javascript 数学 精度 分数 mathjs

评论

1赞 jabaa 5/27/2023
你可以为此编写自己的库。这很简单。
1赞 valerii15298 5/27/2023
你在@jabaa开玩笑吗?decimal.js,fraction.js或mathjs是具有许多功能的相当大的库...编写这样的库肯定不是一件容易的事......无论如何,在社区中拥有这样的库比每个团队单独开发它要好
0赞 T.J. Crowder 5/27/2023
第一个结果看起来像一个错误(我可以复制),因为文档说第一个数字是分子,第二个是分母。您可以考虑举报。(也许可以修复它。
0赞 jabaa 5/27/2023
这取决于您的要求。为这个问题中的需求编写代码将花费不到 2 小时的时间。我在学习中写了一个类似的库,在某些日子里有更多的功能。
3赞 jabaa 5/27/2023
如果你问的问题要么太宽泛了,如何编写代码,要么它要求库推荐,这是题外话。如果您对某个特定库有疑问,可以在此处或 GitHub 页面上提问。目前,您正在询问多个库。

答:

2赞 tevemadar 5/27/2023 #1

这更像是您在 中发现的错误。如果手动提供最大分母参数,则生成 1/3。decimal.js

const a1 = new Decimal(1);
const a2 = new Decimal(3);
console.log(a1.div(a2).toFraction(-1>>>1)); // maxD = largest 32-bit signed int
<script src="https://cdn.jsdelivr.net/gh/MikeMcl/decimal.js/decimal.js"></script>

https://github.com/MikeMcl/decimal.js/issues 似乎是可以报告的地方。

评论

0赞 valerii15298 6/2/2023
似乎是预期的行为 github.com/MikeMcl/decimal.js/issues/221#event-9363428154
5赞 trincot 5/27/2023 #2

鉴于 JavaScript 具有 BigInt 数据类型,因此自己实现基本的算术运算并不难。构造函数中需要大多数逻辑,以便对分数进行规范化(分母不应为负数,分子和分母应为共质数)并处理类型转换。

例如:

class BigFraction {
    #n
    #d
    
    constructor(n, d=1n) {
        const abs = a => a < 0n ? -a : a;
        const gcd = (a, b) => b ? gcd(b, a % b) : a;
        const sign = a => a < 0n ? -1n : 1n;

        // Pass-through
        if (n instanceof BigFraction) return n;
        if (typeof d !== "bigint") throw "Second argument should be bigint when provided";
        if (typeof n !== "bigint") {
            if (arguments.length != 1) throw "Only one argument allowed when first is not a bigint"
            const s = String(n);
            const match = s.match(/^([+-]?\d+)(?:([.\/])(\d+))?$/);
            if (!match) throw `'${s}' not recognised as number`;
            if (match[2] == "/") {
                n = BigInt(match[1]);
                d = BigInt(match[3]);
            } else {
                n = BigInt(s.replace(".", ""));
                d = BigInt("1".padEnd((match[3]?.length ?? 0) + 1, "0"));
            }
        }
        // Normalize
        const factor = sign(n) * sign(d);
        n = abs(n);
        d = abs(d);
        const common = gcd(n, d);
        this.#n = factor * n / common;
        this.#d = d / common;
    }
    add(num) {
        const other = new BigFraction(num);
        return new BigFraction(this.#n * other.#d + other.#n * this.#d, this.#d * other.#d);
    }
    subtract(num) {
        const other = new BigFraction(num);
        return new BigFraction(this.#n * other.#d - other.#n * this.#d, this.#d * other.#d);
    }
    multiply(num) {
        const other = new BigFraction(num);
        return new BigFraction(this.#n * other.#n, this.#d * other.#d);
    }
    divide(num) {
        const other = new BigFraction(num);
        return new BigFraction(this.#n * other.#d, this.#d * other.#n);
    }
    inverse() {
        return new BigFraction(this.#d, this.#n);
    }
    sign() {
        return this.#n < 0n ? -1 : this.#n > 0n ? 1 : 0; 
    }
    negate() {
        return new BigFraction(-this.#n, this.#d);
    }
    compare(num) {
        return this.subtract(num).sign();        
    }
    toString() {
        const s = this.#n.toString();
        return this.#d != 1n ? s + "/" + this.#d : s;
    }
    valueOf() { // Conversion to number data type (float precision)
        return Number(this.#n) / Number(this.#d);
    }    
}

// Demo
var a = new BigFraction("12.34");
console.log("12.34 is " + a);
var b = a.add("-36/5");
console.log("12.34 - 36/5 is " + b);
var c = b.multiply("99128362832913233/9182");
console.log("(12.34 - 36/5) * (99128362832913233/9182) is " + c);