--- title: 'EXTRA-浮點數的計算處理' disqus: hackmd --- EXTRA-浮點數的計算處理 === 綱要 [TOC] 概論 --- 有個很經典的 JavaScript 小數相加問題,也就是 ==**0.1 + 0.2 = 0.30000000000000004**== 這是因為 JavaScript 中浮點數卻又同樣都是 Number,且規範為 IEEE 754,使用 64 位元長度來表示,也就是標準 Double 雙精準度浮點數。 **只要你必須處理報表或多國貨幣之類的轉換,就一定會需要解決這個問題。** 現在來看看 0.1 + 0.2 為何會不完全等於 0.3: 先將 0.1 和 0.2 轉為二進位數: ```javascript= let [firstVal, endVal] = [0.1, 0.2] // 使用toString()轉換二進位 // 0.00011001100110011001100110011001100110011001100110011010 firstVal.toString(2) // 0.0011001100110011001100110011001100110011001100110011010 endVal.toString(2) ``` 二進位兩數相加: ```javascript= let result = firstVal + endVal result.toSting(2) // 得到 0.0100110011001100110011001100110011001100110011001100111 // 再將結果轉換成十進位 result.toSting(10) // 0.30000000000000004 ``` ![](https://i.imgur.com/cur0srq.png) 解決方案 --- 在這個 VueJS 範例,我們以前端日常工作會處理到的 Array Object 為主,計算的部分交給方便的開源工具 [number-precision](https://github.com/nefe/number-precision) **安裝 number-precision** `npm install number-precision --save` **建立基本外觀** ```htmlmixed= <template> <div id="app"> <p>income total: {{ incomeTotal }}</p> <p>payment total: {{ paymentTotal }}</p> <el-table :data="tableData" style="width: 100%" stripe> <el-table-column prop="id" label="ID"></el-table-column> <el-table-column prop="income" label="Income"></el-table-column> <el-table-column prop="payment" label="Payment"></el-table-column> </el-table> </div> </template> ``` **Mock 一個資料結構** ```javascript= export default { data() { return { tableData: [], // 仿照API回傳的內容mock一個模擬資料 res: { result: '1', page: '1', pageLimit: '20', data: [ { id: "0", income: "370800.9644", payment: "1206880.8788" }, { ... } // 假設這裡有好幾十筆同樣格式的物件在Array裡面 ] } incomeTotal: '', paymentTotal: '' } } // methods... } ``` **將陣列中的每個物件遍歷處理** ```javascript= import NP from 'number-precision' export default { // data() {...} methods: { columnSummary() { // 模擬AXIOS Request,res.result = 1,成功戳回資料時table要寫入資料 this.tableData = [...this.res.data] // Array.map()會將對象轉為新的陣列,所以在這裡把兩個要統計的欄位定義兩個新陣列 let incomeArr = [] let payoutArr = [] // 遍歷出指定Keys的value並且強制轉型 this.tableData.map( item => { // 利用JS做字串和運算子計算時,會強制轉型的特性,把轉型後的數值全部丟入新陣列 incomeArr.push(item.income * 1) payoutArr.push(item.payout * 1) // 此時的incomeArr和payoutArr會是 [370800.9644, xxxx, ...] 之類的結果 }) // 利用 Array.reduce() 做加總 const sumArr = (arr) => { // 輪到 number-precision 表演精確加總了 return arr.reduce((a, b) => NP.plus(a, b)) } this.incomeTotal = sumArr(incomeArr) this.paymentTotal = sumArr(paymentArr) // 此時this.incomeTotal和this.paymentTotal的結果就會是總計的結果 } }, created() { // 通常資料在created階段就能戳API得到res的內容,所以在這執行 // 如果有Pagination頁碼切換事件,也要記得在該事件調用一次 this.columnSummary() } } ``` 假如只要取到小數第二位,可以利用`Math.floor()` ```javascript= this.incomeTotal = Math.floor(sumArr(incomeArr) * 100) / 100 this.paymentTotal = Math.floor(sumArr(paymentArr) * 100) / 100 ``` **That's it.** ###### tags: `VueJS` `decimal` `number-precision` `reduce`