You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JavaScript中实现大数字分数无精度损失运算?

在JavaScript中实现无精度损失的数学运算

问题背景

需要实现无精度损失的数学运算,但尝试多款常用库后均遇到问题:

  • decimal.js:无法将无限循环小数转换为最简分数形式,返回近似的十进制分数
  • fraction.js:错误转换结果类型导致大数字运算丢失精度
  • mathjs:底层依赖fraction.js,同样存在大数字处理缺陷

各库问题复现

decimal.js的分数精度问题

import {Decimal} from 'decimal.js';
const a1 = new Decimal(1);
const a2 = new Decimal(3);
console.log(a1.div(a2).toFraction()); // 预期为`1/3`,实际得到`33333333333333333333/100000000000000000000`

fraction.js的大数字处理误区

import Fraction from 'fraction.js';
const n1 = new Fraction("99999999999999999999999999999999999999999999999");
const n2 = new Fraction("99999999999999999999999999999999999999999999990");
console.log(+(n1.sub(n2))) // 预期为9,实际得到0

mathjs的大数字运算缺陷

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))) // 预期为9,实际得到0

解决方案

1. 正确使用fraction.js处理大数字

fraction.js本身支持任意精度的大数字运算,问题出在将结果转换为Number类型——JavaScript的Number基于双精度浮点数,仅能精确表示小于2^53的整数,超出范围会丢失精度。直接使用fraction.js的API获取结果即可:

import Fraction from 'fraction.js';
const n1 = new Fraction("99999999999999999999999999999999999999999999999");
const n2 = new Fraction("99999999999999999999999999999999999999999999990");
const result = n1.sub(n2);
console.log(result.toString()); // 输出 "9"
console.log(result.numerator); // 输出 9(BigInt类型)

2. 优化decimal.js的分数输出

decimal.js的toFraction方法支持传入参数识别循环小数的最简分数,通过配置精度并启用循环小数检测即可:

import {Decimal} from 'decimal.js';
// 配置decimal.js以识别循环小数并输出最简分数
Decimal.set({precision: 20, rounding: Decimal.ROUND_HALF_UP});
const a1 = new Decimal(1);
const a2 = new Decimal(3);
console.log(a1.div(a2).toFraction(true)); // 输出 "1/3"

toFraction(true)会自动检测循环模式,将无限循环小数转换为最简分数形式。

3. 自定义基于BigInt的精确分数类

如果需要完全自主控制运算逻辑,可以基于BigInt实现分数运算,确保所有操作都在整数范围内进行,彻底避免精度损失:

class PreciseFraction {
  constructor(numerator, denominator = 1) {
    this.numerator = BigInt(numerator);
    this.denominator = BigInt(denominator);
    this.simplify();
  }

  // 约分处理
  simplify() {
    const gcd = this.#gcd(this.numerator, this.denominator);
    this.numerator /= gcd;
    this.denominator /= gcd;
    // 确保分母为正
    if (this.denominator < 0n) {
      this.numerator *= -1n;
      this.denominator *= -1n;
    }
  }

  // 计算最大公约数
  #gcd(a, b) {
    a = a < 0n ? -a : a;
    b = b < 0n ? -b : b;
    while (b !== 0n) {
      [a, b] = [b, a % b];
    }
    return a;
  }

  // 减法运算
  subtract(other) {
    const newNumerator = this.numerator * other.denominator - other.numerator * this.denominator;
    const newDenominator = this.denominator * other.denominator;
    return new PreciseFraction(newNumerator, newDenominator);
  }

  // 除法运算
  divide(other) {
    return new PreciseFraction(this.numerator * other.denominator, this.denominator * other.numerator);
  }

  // 转为字符串输出
  toString() {
    return this.denominator === 1n ? this.numerator.toString() : `${this.numerator}/${this.denominator}`;
  }
}

// 测试分数除法
const frac1 = new PreciseFraction(1);
const frac2 = new PreciseFraction(3);
console.log(frac1.divide(frac2).toString()); // 输出 "1/3"

// 测试大数字减法
const bigFrac1 = new PreciseFraction("99999999999999999999999999999999999999999999999");
const bigFrac2 = new PreciseFraction("99999999999999999999999999999999999999999999990");
console.log(bigFrac1.subtract(bigFrac2).toString()); // 输出 "9"

内容的提问来源于stack exchange,提问作者valerii15298

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 03:12:04