1. 程式人生 > 其它 >js小數點計算丟失精度

js小數點計算丟失精度

  有時需求中會有前端校驗輸入數字金額的時候,判斷,幾個輸入框的金額合計是否大於小於或等於某個整數,在輸入的值可以為小數的時候,很容易就出現js小數點計算丟失精度問題。比如下圖

  js高階程式設計(我這版是第3版)在3.4.5Number型別這節中就談到了這個現象,原話是:

    關於浮點數值計算會產生攝入誤差的問題,有一點需要明確:這是使用基於IEEE754數值的浮點計算的通病,ESMAScript並非獨此一家,其他使用相同數值格式的語言也存在這個問題。

  所以即使浮點數值的最高精度是17位小數,但在進行算術計算時其精度遠遠不如整數。

  1.使用toFixed(x)方法,x為必需,規定小數的位數,是 0 ~ 20 之間的值,包括 0 和 20,如果省略了該引數,將用 0 代替。

  比如:

  但是這種方法的侷限性是不能使用toFixed(x)去進行舍入操作。因為IEEE754標準規定的浮點數取整演算法是銀行家舍入法,即四捨六入五留雙法:四捨六入五考慮,五後非零就進一,五後為零看奇偶,五前為偶應捨去,五前為奇要進一。

  早期的時候似乎在不同瀏覽器會有不同的表現,但是現在基本上大部分瀏覽器都是toFixed(x)遇5不會進1,而是直接捨去,可能也和chrome核心一家獨大有關係。在Chrome瀏覽器中的控制檯表現如下:

  精度只能用於所有運算元中最多小數位的精度計算,即toFixed(x)中的x必須大於等於最多小數位運算元的小數位數量,否則就會丟失精度。

  2.使用第三方庫

  比如Math.js,decimal.js,使用方法大同小異,詳情見:

  https://www.npmjs.com/package/mathjs

  https://www.npmjs.com/package/decimal.js/v/3.0.0

  以vue2.x為例:

  先npm install mathjs / decimal.js

  然後在main.js中引入,以ES模組規範為例:

  math.js是這樣使用的:(詳見math.js文件:https://mathjs.org/docs/index.html)

import * as math from "mathjs";

const config = {
  epsilon: 
1e-12, matrix: 'Matrix', number: 'number', //運算時需要精度準確時此處需配置為BigNumber precision: 64, //僅在number型別為BigNumbers生效 predictable: false, randomSeed: null //選項設定為種子偽隨機數生成,使其成為確定性的。 } const math = create(all, config); let number = math.add(math.bignumber(0.1), math.bignumber(0.2)); //運算元中至少要有一個呼叫bignumber()

  decimal.js是這樣使用的:(詳見decimal.js文件:http://mikemcl.github.io/decimal.js/)

import { Decimal } from 'decimal.js';

let number = new Decimal(0.11).add(new Decimal(0.29));

其他

  本來到前面就到尾聲了,但是這裡還是需要囉嗦一下,我還看過一些部落格還提到一個方法或是自己封裝的方法,其核心是先將小數*10的n次方放大為整數,只要最終結果在Number型別邊界之內的整數運算是不會有精度誤差的,然後再將整數的運算結果除於10的n次方就得到最終的結果。但是,這個方法沒寫好也是存在問題的,就比如前面舉的例子:0.07*100,結果就不是7。因為只有少數幾個小數可以被浮點數精確表示,比如0.25的N次方,或是0.75的n次方,其他的小數基本都是近似數。其實我也很納悶0.06,0.08也都是近似數,但是他們相乘100結果分別就是6,8。有些博主通過這個方法封裝的方法都沒考慮到這塊,所以在相乘放大過程中就已經出錯了,但是如果在放大縮小過程中能控制住精度,也仍不失為一個解決問題的好方法。