【大数处理】原理探析及模拟实现

797 阅读3分钟

Hello, 各位勇敢的小伙伴, 大家好, 我是你们的嘴强王者小五, 身体健康, 脑子没病.

本人有丰富的脱发技巧, 能让你一跃成为资深大咖.

一看就会一写就废是本人的主旨, 菜到抠脚是本人的特点, 卑微中透着一丝丝刚强, 傻人有傻福是对我最大的安慰.

欢迎来到 小五随笔系列【大数处理】原理探析及模拟实现.

前言

0.1+0.2=0.300000000000000040.1 + 0.2 = 0.30000000000000004,一个大家刚入门前端时就会接触到的问题,原因是小数精度丢失

实际工作中,我们会引入诸如 big.js 的库来做相应处理,那大家有没有探索过这类库是怎么实现的呢?如果没有,不妨跟着笔者的步伐,一行一行的敲出一个属于自己的大数处理库

双手奉上代码链接【Github - ajun568】

ts2.gif

tips:本文仅实现 “加减乘除” 四个基本操作,其余操作大同小异,感兴趣的话大家可自行实现

数据构造

核心思路: 将数字转换为十以内整数的加减法运算,这样既不存在小数点精度问题,也不存在范围超出问题,进而实现了大数处理。

存储格式: Big {sign: 1, pow: 2, data: [1, 2]}

👉 以120为例

  • sign:正负号,1代表正数,-1代表负数

    • 120为正数,故sign为1
  • pow:幂,默认为0

    • 120=1.2102120 = 1.2 * 10^2,故pow为2,这里底数应 1<=n<101<=n<10(0除外)
  • data:数据

    • 拆解数字存入数组,并抹掉首尾的零,得到 [1,2][1, 2]

数据处理:

鉴于 signsigndatadata 的逻辑都比较简单,我们先思考一下 powpow 的逻辑

👉 我们穷举一些例子做观察:

  • 120=1.2102pow=2120 = 1.2 * 10^{2} \Rightarrow pow = 2
  • 12.12=1.212101pow=112.12 = 1.212 * 10^{1} \Rightarrow pow = 1
  • 1.2=1.2100pow=01.2 = 1.2 * 10^{0} \Rightarrow pow = 0
  • 0.012=1.2102pow=20.012 = 1.2 * 10^{-2} \Rightarrow pow = -2

👉 可以发现如下规律:

  • 整数的 powpow 即为 str.length - 1

  • 小数的 powpow 则取决于小数点的位置,每向后移一位 pow++,每向前移一位 pow--

👉 特殊处理 -- 科学计数法

12.37e2=12.3710212.37e^{-2} = 12.37 * 10^{-2},我们只需找到 e 的位置,e 前的数字按上述处理,e 后的数字为需要额外追加的幂

🦥 翠花上代码

processParam.png

数据还原:

数据还原则是对数据处理的逆向操作,通过 powpowdata.lengthdata.length 即可确认下述内容

  • pow<0pow < 0\Rightarrow 待还原的数字小于1,向头部补零并追加小数点
  • pow>=0pow >= 0
    • pow>=data.lengthpow >= data.length\Rightarrow 整数,按需向尾部补零
    • pow<data.lengthpow < data.length\Rightarrow 小数,追加小数点

🦥 翠花上代码

valueOf.png

32.79+0.1232.79 + 0.12,我们图解一下它的运算过程

加法.png

原理:逐位相加,有进位加进位

说明:

  • 符号相反 \Rightarrow 减法,eg3+(2)=32eg:3 + (-2) = 3 - 2

  • 通过 powpow 对齐位数

  • 若最后仍有进位,需处理进位,eg9+8=17eg:9 + 8 = 17

  • 注意我们的约定格式,若结果数据 datadata 前后有多余的零,需处理

🦥 翠花上代码

plus.png

32.910.1232.91 - 0.12,我们图解一下它的运算过程

减法.png

原理:逐位相减,有借位减借位

说明:

  • 减法中被减数应大于减数,eg23(32)eg:2 - 3 \Rightarrow -(3-2),相等则直接返回 00

  • 符号相反 \Rightarrow 加法

    • 2(2)=2+22 - (-2) = 2 + 2
    • 22=(2)+(2)-2 - 2 = (-2) + (-2)
  • 注意我们的约定格式,若结果数据 datadata 前后有多余的零,需处理

🦥 翠花上代码

minus.png

32×6932 × 69,我们图解一下它的运算过程

乘法.png

原理:乘法即加法,逐位相乘,逐位相加

说明:

  • 若相乘的两数中有零,直接返回零

  • 两数字相乘的最大位数是 data1.length+data2+lengthdata1.length + data2+length,最大幂是 pow1+pow2+1pow1 + pow2 + 1

  • 按位相乘后,写入对应位置,并做加法处理

  • 处理结果数据 datadata 首尾的零

🦥 翠花上代码

times.png

2208÷692208 ÷ 69,我们图解一下它的运算过程

除法.png

原理:除法即减法,逐次相减,记录次数

说明:

  • 除数不能为零

  • 被除数为零,直接返回零

  • 幂为被除数的幂减除数的幂,若被除数小于除数,则借位减一

  • dpdp \Rightarrow 小数点后的保留位数,假设 dp=8dp = 820÷32,000,000,000÷320 ÷ 3 \Rightarrow 2,000,000,000 ÷ 3

  • 减法运算调用前面已实现的 minus 方法

  • 处理结果数据 datadata 首尾的零

🦥 翠花上代码

div.png

other28.gif