提问人:valerii15298 提问时间:5/27/2023 最后编辑:valerii15298 更新时间:5/27/2023 访问量:224
JS fractions for big numbers with no precision loss [已关闭]
JS fractions for big numbers with no precision lose [closed]
问:
支持分数的大数字:
我想在不损失精度的情况下执行数学运算。 如何使用 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.js
fraction.js
我检查了引擎盖下的使用,所以对于大量数字没有任何好处:mathjs
fraction.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
答:
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);
评论