<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          你可能需要一個四舍五入的工具函數(shù)

          共 4997字,需瀏覽 10分鐘

           ·

          2022-07-24 13:48

          ?

          ?    

          目前存在什么問題

          問題:toFixed函數(shù)可以滿足一部分小數(shù)的四舍五入,首先可以看下mdn對于 Number.prototype.toFixed()[1] 的定義。

          mdn的例子也有這個例子

          2.55.toFixed(1)      // 返回 '2.5'. Note it rounds down - see warning above

          警告:浮點數(shù)不能精確地用二進制表示所有小數(shù)。這可能會導致意外的結果,例如 0.1 + 0.2 === 0.3 返回 false .

          mdn的說法是 浮點數(shù)的小數(shù)計算會出現(xiàn)異常。因此toFixed函數(shù)并不能滿足嚴格意義上的四舍五入。

          為什么不使用下面的方法進行四舍五入

          const round = (num: number, decimal = 2): string => {
            const rate = 10 ** decimal;
            const temp = Math.round(num * rate) / rate;
            let strNum = String(temp);
            const numArr = strNum.split('.');
            if (!numArr[1]) {
              strNum += '.';
              strNum = strNum.padEnd(strNum.length + decimal, '0');
            } else if (numArr[1].length < decimal) {
              strNum = strNum.padEnd(numArr[0].length + 1 + decimal, '0');
            }
            return strNum;
          };

          這樣處理的核心代碼是 Math.round(num * 10 ** decimal;) / 10 ** decimal;

          其實這個可以滿足大部分場景,但仍然有兩個小問題:

          1. 如果num本身沒超過 Number.MAX_SAFE_INTEGER 但是 乘以 rate 以后超過了,則可能又會發(fā)生一些意料之外的case
          2. 對于某些場景還是無法處理,如 1.255 保留兩位小數(shù),主要原因是也發(fā)生了精度丟失。

          0.1 + 0.2 !== 0.3 的具體原因

          JavaScript 中所有數(shù)字包括整數(shù)和小數(shù)都只有一種類型 — Number。它的實現(xiàn)遵循 IEEE 754 標準,使用 64 位固定長度來表示,也就是標準的 double 雙精度浮點數(shù)。

          整個計算過程要經(jīng)歷以下幾個步驟:

          十進制轉(zhuǎn)二進制

          先把0.1轉(zhuǎn)換為二進制,見下圖:

          這個處理過程是一個無限循環(huán)的狀態(tài),可以在這直接查看結果[2],最后結果是0.0001100110011001100110011001100110011001100110011001101...

          0011 將會無限循環(huán)

          二進制轉(zhuǎn)科學記數(shù)法

          1.1(0011)… * 2^-4(小數(shù)點向右移4位,二進制中底數(shù)為2)

          對科學記數(shù)法數(shù)據(jù)的二進制表示

          64位存儲科學記數(shù)法

          第一位是符號位,0是正數(shù),1是負數(shù),(-1的0次方還是1次方),case里就是 0

          其后的11位(指數(shù)部分)用于存儲科學記數(shù)法中指數(shù)的二進制數(shù),11位的存儲范圍是 Math.pow(2, 11), 即2048,其中以1023作為正負分界線,這個case里,-4 就是 1023-4 = 1019,轉(zhuǎn)換成二進制后為:01111111011

          剩余的52位(尾數(shù)部分)用于存儲科學記數(shù)法中尾數(shù)小數(shù)點后52位

          所以0.1的二進制是

          0 01111111011 1001100110011001100110011001100110011001100110011010

          同理0.2的二進制是

          0 01111111100 1001100110011001100110011001100110011001100110011010

          對階運算

          0.1的指數(shù)是-4,0.2的指數(shù)是-3。要想將他們運算的結果也采用科學記數(shù)法的方法表示,就得將指數(shù)統(tǒng)一然后提取公因數(shù)進行計算。這里就涉及到一個對階運算[3],為了盡可能減小精度損失,需要遵守小階對大階(即將較小的指數(shù)轉(zhuǎn)換為較大的指數(shù))的原則。在這個問題中,我們要將指數(shù)統(tǒng)一成-3。因此,0.1在經(jīng)過對階操作后的二進制,是這樣的:

          0 01111111100 (0.)1100110011001100110011001100110011001100110011001101

          尾數(shù)需要向右移一位,右移超出的部分進行 0舍1入 運算。默認省略的整數(shù)部分的 1 被移到小數(shù)部分了,因此整數(shù)部分變成了0。

          二進制加法運算

          舍入運算

          這個結果有兩個問題:

          1. 不符合科學記數(shù)法的規(guī)則。
          2. 尾數(shù)部分存在超出位數(shù)的情況。

          因此要對結果做出調(diào)整,首先將結果變?yōu)椤?.”開頭的,即小數(shù)點向左移一位,變成:

          1.00110011001100110011001100110011001100110011001100111

          同時,要將指數(shù)加1:變成:

          01111111101

          最后,依然根據(jù)0舍1入的原則,將尾數(shù)部分超出52位以外的部分做舍入運算,結果為:

          1.0011001100110011001100110011001100110011001100110100

          因此,最終的完整結果為:

          0 01111111101 (1.)0011001100110011001100110011001100110011001100110100

          最高位為 1,得到的二進制數(shù)如下所示:

          2^-2 * 1.0011001100110011001100110011001100110011001100110100

          二進制轉(zhuǎn)十進制

          轉(zhuǎn)換為十進制即為:

          0.30000000000000004

          做舍入操作,無可避免的會引起精度丟失

          四舍五入函數(shù)如何避免這個問題

          由于四舍五入在統(tǒng)計數(shù)據(jù)時十分常見,所以你可能需要這樣一個函數(shù),來實現(xiàn)完美的四舍五入

          主要做了以下操作:

          1. 把所有數(shù)字轉(zhuǎn)換成一個 number[];
          2. 從最后一個數(shù)字開始計算是否 > 4;
          3. 如果 <= 4 則 break;
          4. 如果 > 4 則往遍歷一位,+1;
          5. 再判斷 +1 后的值是否 === 10
          6. 如果不是 10 則 break;
          7. 如果當前值是 10 ,則變成 0,并記錄下是否是在第一位,即:在最前方補1;
          8. 再接著for循環(huán),i--;然后 +1,直到打破循環(huán);
          // ...

          // 核心代碼
          // 匹配出所有的數(shù)字 是個 int[] 1.223  [1,2,2,3]
          const numArr = zeroStrNum.match(/\d/g) || [];
          // 從最后一位數(shù)字是否大于4算起
          if (parseInt(numArr[numArr.length - 1], 10) > 4) {
            // 如果最后一位大于4,則往前遍歷+1
            for (let i = numArr.length - 2; i >= 0; i--) {
              numArr[i] = String(parseInt(numArr[i], 10) + 1);
              // 判斷這位數(shù)字 +1 后會不會是 10
              if (numArr[i] === '10') {
                // 10的話處理一下變成 0,再次for循環(huán),相當于給前面一個 +1
                numArr[i] = '0';
                // 是否是進位到最前面,zeroStrNum在開頭補的0了
                flag = i !== 1;
              } else {
                // 小于10的話,就打斷循環(huán),進位成功
                break;
              }
            }
          }

          // ...

          參考文獻:

          1. https://zhuanlan.zhihu.com/p/103254614
          2. https://zhuanlan.zhihu.com/p/363254961
          3. https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed
          4. https://www.boatsky.com/blog/26

          參考資料

          [1]

          Number.prototype.toFixed(): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed

          [2]

          可以在這直接查看結果: https://tool.oschina.net/hexconvert

          [3]

          對階運算: https://www.cnblogs.com/yilang/p/11277201.html

          - END -

          ?? 謝謝支持

          以上便是本次分享的全部內(nèi)容,希望對你有所幫助^_^

          喜歡的話別忘了 分享、點贊、收藏 三連哦~。

          歡迎關注公眾號 趣談前端 收貨大廠一手好文章~

          ?



          從零搭建全棧可視化大屏制作平臺V6.Dooring

          從零設計可視化大屏搭建引擎

          Dooring可視化搭建平臺數(shù)據(jù)源設計剖析

          可視化搭建的一些思考和實踐

          基于Koa + React + TS從零開發(fā)全棧文檔編輯器(進階實戰(zhàn)




          點個在看你最好看


          ?

          瀏覽 78
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久精品一区二区 | 国产操屄直播 | 琪琪五月丁香 | 日日夜夜人人爽 | 日本欧美中文 |