如何实现BigInt对float取模的bigModulo函数?
实现BigInt对浮点数的取模运算
要解决BigInt对Number(浮点数)取模时的精度丢失和类型错误问题,我们可以通过将浮点数转换为精确分数,结合BigInt的高精度运算来实现正确的取模结果。
实现代码
function numberToExactFraction(d: number): { numerator: bigint, denominator: bigint } { if (d === 0) return { numerator: 0n, denominator: 1n }; // 处理符号 const sign = d < 0 ? -1n : 1n; d = Math.abs(d); // 分解双精度浮点数的二进制结构 const bits = new Uint32Array(new Float64Array([d]).buffer); const exponentBits = (bits[1] >> 20) & 0x7FF; const mantissaBits = ((bits[1] & 0xFFFFF) << 32) | bits[0]; let mantissa: bigint; let exponent: number; if (exponentBits === 0) { // 非规格化数 mantissa = BigInt(mantissaBits); exponent = -1074; // 1 - 1023 - 52 } else { // 规格化数,隐含最高位的1 mantissa = (1n << 52n) | BigInt(mantissaBits); exponent = exponentBits - 1023 - 52; } // 转换为分数形式:sign * mantissa * 2^exponent = numerator / denominator let numerator = sign * mantissa; let denominator = 1n; if (exponent > 0) { numerator <<= BigInt(exponent); } else if (exponent < 0) { denominator <<= BigInt(-exponent); } // 约分(最大公约数) const gcd = (a: bigint, b: bigint): bigint => { while (b !== 0n) { [a, b] = [b, a % b]; } return a; }; const commonDivisor = gcd(numerator < 0 ? -numerator : numerator, denominator); numerator /= commonDivisor; denominator /= commonDivisor; return { numerator, denominator }; } function bigModulo(numerator: bigint, denominator: number): number { if (denominator === 0) { throw new Error("分母不能为0"); } const { numerator: p, denominator: q } = numberToExactFraction(denominator); if (p === 0n) { throw new Error("分母不能为0"); } // 计算 (numerator * q) mod p,确保所有运算用BigInt避免精度丢失 const product = numerator * q; let remainder = product % p; // 可选:确保结果符号与分母一致(数学上的非负模) // if ((denominator > 0 && remainder < 0) || (denominator < 0 && remainder > 0)) { // remainder += p; // } // 将余数转换为Number类型返回 return Number(remainder) / Number(q); }
原理说明
浮点数转精确分数:
JS的Number是64位双精度浮点数,本质是二进制分数。我们通过解析其二进制结构(符号位、指数位、尾数位),将其转换为精确的分子/分母形式(均为BigInt),并约分得到最简分数。数学推导:
假设浮点数denominator=p/q(p、q为整数),那么BigIntnumerator对它的取模可以转化为:
$$\text{result} = \frac{(numerator \times q) \mod p}{q}$$
所有中间运算用BigInt执行,完全避免了精度丢失。结果转换:
取模结果的绝对值必然小于denominator的绝对值,因此可以安全转换为Number类型,不会丢失精度。
测试示例
// 测试用户提供的示例 const testNumerator = 10_000_000_000_000_000_004n; const testDenominator = 1.43; console.log(bigModulo(testNumerator, testDenominator));
注:由于JS中1.43是二进制近似值,若需基于十进制精确值143/100计算,可直接传入分数形式的分子分母进行运算,或调整代码优先处理十进制小数。
内容的提问来源于stack exchange,提问作者Yukulélé
相关产品推荐
相关产品推荐

