bigdecimal 保留两位小数_一起聊聊小数的储存和运算
原創(chuàng): 蜀中亮子 玄說前端
小數(shù)運算的問題
在 js 中的小數(shù)運算中,一直存在著一個問題,
比如:0.1+0.2=0.30000000000000004 、0.4-0.3=0.10000000000000003。
那么為什么會出現(xiàn)這種情況呢?這種情況又如何解決呢?
為什么?
這就要講到 js 小數(shù)的存儲,在 js 找那個所有的數(shù)字包括小數(shù)和整數(shù)都只有一種類型—Number。它實現(xiàn)遵循 IEEE 754 標(biāo)準(zhǔn)。使用 64 位固定長度來表示。
這樣的存儲結(jié)構(gòu)優(yōu)點是可以歸一化處理整數(shù)和小數(shù),節(jié)省存儲空間。
64 位比特又可分為三個部分:
- 符號位 S:第 1 位是正負(fù)數(shù)符號位(sign),0 代表正數(shù),1 代表負(fù)數(shù)
- 指數(shù)位 E:中間的 11 位存儲指數(shù)(exponent),用來表示次方數(shù)
- 尾數(shù)位 M:最后的 52 位是尾數(shù)(mantissa),超出的部分自動進(jìn)一舍零
實際數(shù)字就可以用以下公式來計算:
注意以上的公式遵循科學(xué)計數(shù)法的規(guī)范,在十進(jìn)制是為 0<M<10,到二進(jìn)制就是 0<M<2。也就是說整數(shù)部分只能是 1,所以可以被舍去,只保留后面的小數(shù)部分(想不通這個道理的,可以看下下面0.1的例子)。
比如:十進(jìn)制 4.5 轉(zhuǎn)換成二進(jìn)制就是 100.1,科學(xué)計數(shù)法表示是 1.001*2^2,舍去 1 后 M = 001;
E 是一個無符號整數(shù),因為長度是 11 位,取值范圍是 0~2047。但是科學(xué)計數(shù)法中的指數(shù)是可以為負(fù)數(shù)的,所以再減去一個中間數(shù) 1023,[0,1022]表示為負(fù),[1024,2047] 表示為正。如 4.5 的指數(shù) E = 1025,尾數(shù) M 為 001。
最終的公式變成:
所以 4.5 最終表示為(S=1、E=1025、M=001);
再以 0.1 例解釋浮點誤差的原因, 0.1 轉(zhuǎn)成二進(jìn)制表示為 0.0001100110011001100(1100 循環(huán)),1.100110011001100x2^-4,所以 E=-4+1023=1019;M 舍去首位的 1,得到 100110011...。轉(zhuǎn)化成十進(jìn)制后為 0.100000000000000005551115123126,因此就出現(xiàn)了浮點誤差。
那小數(shù)是怎么轉(zhuǎn)為二進(jìn)制的呢?
小數(shù)轉(zhuǎn)二進(jìn)制
比如數(shù)字 3.2 轉(zhuǎn)為二進(jìn)制的過程;
- 第一步:0.32*2=0.64,這個時候整數(shù)部分是 0,所以第一位是 0,那么就是 0.0
- 第二步:0.64*2=1.28,這個時候整數(shù)部分是 1,所以第二位是 1,那么就是 0.01
- 第三步:把取了一之后的 1.28 減去 1 之后轉(zhuǎn)換為 0.28,拿這個部分再乘以二,那么就是 0.28*2=0.56,這個時候整數(shù)部分是 0,所以第三位是 0,那么就成了 0.010
- 第四步:0.56*2=1.12,這個時候整數(shù)部分是 1,所以第四位是 1,那么就是 0.0101,
- 第五步:和第三步的思路一樣 這樣一直到最后小數(shù)部分沒有了的時候,就算是轉(zhuǎn)換完成。為什么要乘以二,因為使用二進(jìn)制數(shù)據(jù)表示數(shù)的時候,只有兩位,0 和 1。
怎么解決這個問題
第一種
把小數(shù)轉(zhuǎn)成整數(shù)后再運算。以加法為例:
0.1+0.2
把數(shù)字都乘以他們可以轉(zhuǎn)換為整數(shù)的倍數(shù),計算之后,再除以乘上的倍數(shù)。比如:(0.1*10+0.2*10)/10
第二種
還有另一個方案,是做一個自己的計算過程,比如 0.1+0.2,首先通過正則或者 AST 或者其他的解析方式,匹配出了符號和數(shù)字,再把字符按照四則運算法的規(guī)律來計算,0.1 和 0.2 取小數(shù)位后第一位相加減,是否進(jìn) 1,發(fā)現(xiàn)不需要去整數(shù)位第一位加減,依次遞推。這個方法的思路就和我們豎式計算一樣,把豎式計算的思路,代碼化。至此,小數(shù)的計算與原因都找到了。拓展一下,在計算機底層 cpu 是怎么進(jìn)行加減乘除運算的呢?可以進(jìn)群討論最后附上想進(jìn)前端群的可以加微信,備注:玄說前端。
END
獲得更多信息關(guān)注公眾號
總結(jié)
以上是生活随笔為你收集整理的bigdecimal 保留两位小数_一起聊聊小数的储存和运算的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 知识图谱论文阅读【十二】【KDD2020
- 下一篇: python complex函数def_