likes
comments
collection
share

造轮子之大数浮点数计算

作者站长头像
站长
· 阅读数 6

JavaScript浮点数溢出问题及其解决方案的开源库介绍

在JavaScript中,浮点数溢出是一个常见且棘手的问题。这主要是由于JavaScript使用IEEE 754双精度浮点数格式来表示数字,这种格式虽然可以表示非常大或非常小的数字,但仍然有其局限性。当数字超出这个范围时,就会发生溢出,导致结果不准确或不可预测。

如: 0.1+0.2 在Javascript中得不到0.3这个理所当然的结果.

0.1+0.2==0.3   // 在js中返回的是false

JavaScript浮点数溢出问题的原因

  1. 表示范围的限制:JavaScript中的Number类型使用64位双精度格式IEEE 754标准,其中52位用于表示有效数字,11位用于表示指数部分。这种格式能够表示的最大正数是 1.7976931348623157e+308,最小正数是 5e-324。超出这个范围的数字会导致溢出。
  2. 精度问题:即使数字在可表示范围内,由于浮点数的二进制表示方式,某些十进制小数无法精确表示,这会导致精度损失。当进行多次运算或大量计算时,这种精度损失可能会累积并放大,最终导致不准确的结果。

我的解决方案

BigInt 是 JavaScript 中的一个新的数字类型,可以用任意精度表示整数。使用 BigInt,即使超出 Number 的安全整数范围限制,也可以安全地存储和操作大整数。

于是我想,既然它能提供足够大的整数计算,那么如果我们能把所有小数计算用BigInt计算来代替,是不是可以解决浮点数溢出的问题?

比如:

1.1 + 0.0012   // 返回1.1012000000000002; 浮点数溢出得不到正确解

我们完全可以将两项中的小数点先向左移动4位数,等于各自放大10000倍,转化为

11000 + 12 = 11012

在得到结果后,将小数往左移动4位数,恢复到它应该出现的位数就好了。

如何更方便使用

在使用类似于 BigNumber 此类开源库的时候,如果运算都需要用方法表达,就会让工具方法显得难用。而我们只是想要一个计算结果,却要对每一项数值进行实例化及方法调用。

const num1 = new BigNumber('0.1');
const num2 = new BigNumber('0.2');
const sum = num1.plus(num2);
console.log(sum.toString());
// 输出:0.3

更为复杂点,混杂着四则混合运算呢?我们该如何高效的得到结果,并且最大程度避免“浮点数溢出问题”。我们是不是可以直接计算一个表达式求解,用最直接的数学式表达得到结果。AnyNumber为我提供的大数浮点数计算的方法库。

const value = AnyNumber('0.1+0.2').value;
console.log(value);
// 输出:0.3

const value = AnyNumber('11.2+24.4+66/(0.1+0.2)').value;
console.log(value);
// 输出:255.6; 原生JS输出为:255.59999999999997

不知道你们是否注意到了如果计算结果的小数位数足够小,JS会自动将数值表达写成科学计数法。

console.log(12**24);
// 输出:7.949684720339084e+25

如果我想要进行大数计算,且不想显示为科学计数法呢?那么,我的方法

let value = AnyNumber('12**24').value;
console.log(value);
// 输出:79496847203390840000000000

我的方法库

提供了常见的加、减、乘、除、取余、开方、幂运算等,并且提供小数点精确度调整、科学计数法、数值格式化等方法。

仓库地址:github.com/mumuy/anynu…

演示地址:passer-by.com/anynumber/

更多开源:passer-by.com/project

AnyNumber() 初始化

运行:AnyNumber(2.4e8)
// => '240000000'

toFormat() 返回格式化数值

运行:AnyNumber(1000000.123456).toFormat(2)
// => '1,000,000.12'

toFixed() 精确度小数点后n位

运行:AnyNumber(3.1415926535).toFixed(2)
// => '3.14'

toString() 返回字符串

运行:AnyNumber(1.2e3).toString()
// => '1200'

toNumber() 返回原生Number对象

运行:AnyNumber('1.28e3').toNumber()
// => 1280

add() 加法运算

运行:AnyNumber('0.1').add('0.2').value
// => '0.3'

subtract() 减法运算

运行:AnyNumber('100').subtract('0.5').value
// => '99.5'

multiply() 乘法运算

运行:AnyNumber('3').multiply('2').value
// => '6'

divide() 除法运算

运行:AnyNumber('100').divide('4').value
// => '25'

mod() 取余运算

运行:AnyNumber('10').mod('3').value
// => '1'

sqrt() 开方运算

运行:AnyNumber('16').sqrt().value
// => '4'

pow() 幂运算

运行:AnyNumber('10').pow('2').value
// => '100'