// 解決浮動運算問題,避免小數點後産生多位數和計算精度損失。
class Precision {
// 乘法
times (num1, num2, ...others) {
if (others.length > 0) {
return this.times(this.times(num1, num2), ...others)
}
const num1Changed = this.float2Fixed(num1)
const num2Changed = this.float2Fixed(num2)
// 把兩個數的小數位數相加
const baseNum = this.digitLength(num1) + this.digitLength(num2)
const leftValue = num1Changed * num2Changed
this.checkBoundary(leftValue)
return leftValue / Math.pow(10, baseNum)
}
// 精确加法
plus (num1, num2, ...others) {
if (others.length > 0) {
return this.plus(this.plus(num1, num2), ...others)
}
const baseNum = Math.pow(10, Math.max(this.digitLength(num1), this.digitLength(num2)))
return (this.times(num1, baseNum) + this.times(num2, baseNum)) / baseNum
}
// 精确減法
minus (num1, num2, ...others) {
if (others.length > 0) {
return this.minus(this.minus(num1, num2), ...others)
}
const baseNum = Math.pow(10, Math.max(this.digitLength(num1), this.digitLength(num2)))
return (this.times(num1, baseNum) - this.times(num2, baseNum)) / baseNum
}
// 精确除法
divide (num1, num2, ...others) {
if (others.length > 0) {
return this.divide(this.divide(num1, num2), ...others)
}
const num1Change = this.float2Fixed(num1)
const num2Change = this.float2Fixed(num2)
return this.times(num1Change / num2Change, Math.pow(10, this.digitLength(num2) - this.digitLength(num1)))
}
// 四舍五入,且保留小數
round (num, ratio) {
const base = Math.pow(10, ratio)
return this.divide(Math.round(this.times(num, base)), base)
}
// 把小數轉成整數,支援科學計數法。如果是小數則放大成整數
float2Fixed (num) {
if (~num.toString().indexOf('e')) {
return Number(num.toString().replace('.', ''))
}
const dlen = this.digitLength(num)
return dlen > 0 ? num * Math.pow(10, dlen) : num
}
// 擷取目前數小數位的長度(處理科學計數法,本質上處理e-n的情況)
digitLength (num) {
const eSplit = num.toString().split(/[eE]/)
const len = (eSplit[0].split('.')[1]|| '').length - (+ eSplit[1] || 0)
return len > 0 ? len : 0
}
// 檢測數字是否越界,如果越界給出提示
checkBoundary(num) {
if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
console.log(`${num} is beyond boundary when transfer to integer, the results may not be accurate`)
}
}
}
let precision = new Precision()
console.log(precision.times(0.1, 0.2, 0.3))
console.log(precision.plus(0.1, 0.2))
console.log(precision.minus(0.1, 0.2))
console.log(precision.divide(0.1, 0.2))