---
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
```

解決方案
---
在這個 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`