/**
* floatOPS 包含加減乘除四個(gè)方法,能確保浮點(diǎn)數(shù)運(yùn)算不丟失精度
*
* 我們知道計(jì)算機(jī)編程語言里浮點(diǎn)數(shù)計(jì)算會(huì)存在精度丟失問題(或稱舍入誤差),其根本原因是二進(jìn)制和實(shí)現(xiàn)位數(shù)限制有些數(shù)無法有限表示
* 以下是十進(jìn)制小數(shù)對(duì)應(yīng)的二進(jìn)制表示
*? ? ? 0.1 >> 0.0001 1001 1001 1001…(1001無限循環(huán))
*? ? ? 0.2 >> 0.0011 0011 0011 0011…(0011無限循環(huán))
* 計(jì)算機(jī)里每種數(shù)據(jù)類型的存儲(chǔ)是一個(gè)有限寬度,比如 JavaScript 使用 64 位存儲(chǔ)數(shù)字類型,因此超出的會(huì)舍去。舍去的部分就是精度丟失的部分。
*
* ** method **
*? add / subtract / multiply /divide
*
* ** explame **
*? 0.1 + 0.2 == 0.30000000000000004 (多了 0.00000000000004)
*? 0.2 + 0.4 == 0.6000000000000001? (多了 0.0000000000001)
*? 19.9 * 100 == 1989.9999999999998 (少了 0.0000000000002)
*
* floatOPS.add(0.1, 0.2) >> 0.3
* floatOPS.multiply(19.9, 100) >> 1990
*
*/
var floatOPS = function () {
? ? /*
? ? * 判斷obj是否為一個(gè)整數(shù)
? ? */
? ? function isInteger(obj) {
? ? ? ? return Math.floor(obj) === obj
? ? }
? ? /*
? ? * 將一個(gè)浮點(diǎn)數(shù)轉(zhuǎn)成整數(shù),返回整數(shù)和倍數(shù)。如 3.14 >> 314,倍數(shù)是 100
? ? * @param floatNum {number} 小數(shù)
? ? * @return {object}
? ? *? {times:100, num: 314}
? ? */
? ? function toInteger(floatNum) {
? ? ? ? var ret = { times: 1, num: 0 }
? ? ? ? var isNegative = floatNum < 0
? ? ? ? if (isInteger(floatNum)) {
? ? ? ? ? ? ret.num = floatNum
? ? ? ? ? ? return ret
? ? ? ? }
? ? ? ? var strfi = floatNum + ''
? ? ? ? var dotPos = strfi.indexOf('.')
? ? ? ? var len = strfi.substr(dotPos + 1).length
? ? ? ? var times = Math.pow(10, len)
? ? ? ? var intNum = parseInt(Math.abs(floatNum) * times + 0.5, 10)
? ? ? ? ret.times = times
? ? ? ? if (isNegative) {
? ? ? ? ? ? intNum = -intNum
? ? ? ? }
? ? ? ? ret.num = intNum
? ? ? ? return ret
? ? }
? ? /*
? ? * 核心方法,實(shí)現(xiàn)加減乘除運(yùn)算,確保不丟失精度
? ? * 思路:把小數(shù)放大為整數(shù)(乘),進(jìn)行算術(shù)運(yùn)算,再縮小為小數(shù)(除)
? ? *
? ? * @param a {number} 運(yùn)算數(shù)1
? ? * @param b {number} 運(yùn)算數(shù)2
? ? * @param digits {number} 精度,保留的小數(shù)點(diǎn)數(shù),比如 2, 即保留為兩位小數(shù)
? ? * @param op {string} 運(yùn)算類型,有加減乘除(add/subtract/multiply/divide)
? ? *
? ? */
? ? function operation(a, b, digits, op) {
? ? ? ? var o1 = toInteger(a)
? ? ? ? var o2 = toInteger(b)
? ? ? ? var n1 = o1.num
? ? ? ? var n2 = o2.num
? ? ? ? var t1 = o1.times
? ? ? ? var t2 = o2.times
? ? ? ? var max = t1 > t2 ? t1 : t2
? ? ? ? var result = null
? ? ? ? switch (op) {
? ? ? ? ? ? case 'add':
? ? ? ? ? ? ? ? if (t1 === t2) { // 兩個(gè)小數(shù)位數(shù)相同
? ? ? ? ? ? ? ? ? ? result = n1 + n2
? ? ? ? ? ? ? ? } else if (t1 > t2) { // o1 小數(shù)位 大于 o2
? ? ? ? ? ? ? ? ? ? result = n1 + n2 * (t1 / t2)
? ? ? ? ? ? ? ? } else { // o1 小數(shù)位 小于 o2
? ? ? ? ? ? ? ? ? ? result = n1 * (t2 / t1) + n2
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return result / max
? ? ? ? ? ? case 'subtract':
? ? ? ? ? ? ? ? if (t1 === t2) {
? ? ? ? ? ? ? ? ? ? result = n1 - n2
? ? ? ? ? ? ? ? } else if (t1 > t2) {
? ? ? ? ? ? ? ? ? ? result = n1 - n2 * (t1 / t2)
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? result = n1 * (t2 / t1) - n2
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return result / max
? ? ? ? ? ? case 'multiply':
? ? ? ? ? ? ? ? result = (n1 * n2) / (t1 * t2)
? ? ? ? ? ? ? ? return result
? ? ? ? ? ? case 'divide':
? ? ? ? ? ? ? ? result = (n1 / n2) * (t2 / t1)
? ? ? ? ? ? ? ? return result
? ? ? ? }
? ? }
? ? // 加減乘除的四個(gè)接口
? ? function add(a, b, digits) {
? ? ? ? return operation(a, b, digits, 'add')
? ? }
? ? function subtract(a, b, digits) {
? ? ? ? return operation(a, b, digits, 'subtract')
? ? }
? ? function multiply(a, b, digits) {
? ? ? ? return operation(a, b, digits, 'multiply')
? ? }
? ? function divide(a, b, digits) {
? ? ? ? return operation(a, b, digits, 'divide')
? ? }
? ? // exports
? ? return {
? ? ? ? add: add,
? ? ? ? subtract: subtract,
? ? ? ? multiply: multiply,
? ? ? ? divide: divide
? ? }
}();