金额处理方法:千分位处理,保留n位小数且只舍不入
比如 0.1+0.2=0.1+0.2=0.30000000000000004 这种问题怎么确保进度?
前言
- 我们在处理金额数据的时候经常会需要对其进行“千分位分隔”和“保留小数位”处理,但是如果只是简单的使用 toLocalString 和 toFixed 的话是容易造成一些问题的
- 比如 toLocalString 存在一定的兼容性问题,在我们通过一些混合框架开发移动端应用的时候就可能无效,toFixed 的话则存在进一位的情况,这是因为toFixed是一个四舍六入五成双的方法
- toFixed是一个四舍六入五成双的方法中,"五成双"指的是根据5后面的数字来定,当5后有数时,舍5入1;当5后无有效数字时,需要分两种情况来讲:① 5前为奇数,舍5入1;② 5前为偶数,舍5不进。(0是偶数)
- 而我们对金额进行格式化处理时,除了“千分位分隔”之外,在“保留小数位”时需求是进行直接舍去多余小数而不进一
- 以下便是我提供的一种处理方式
代码在此!!
/**
* 判断是否为数值
* @param {*} str
* @returns
*/
export function isNumber(str) {
if (typeof str === 'number') {
return true
}
if (typeof str !== 'string') {
return false
}
return !isNaN(str) && !isNaN(parseFloat(str))
}
/**
* 转数值
* @param {*} str
* @returns
*/
export function toNumber(str) {
return isNumber(str) ? Number(str) : 0
}
/**
* 求和函数
* @param {*} arr
* @param {*} property
* @param {*} digit 用于控制精度,即每次求和计算时截取的小数位
* @returns
*/
export function calcSum(arr, property, _digit = 2) {
// 用于避免求和精度问题,比如 0.1+0.2=0.30000000000000004
function formatFloat(f, digit = _digit) {
var m = Math.pow(10, digit)
return parseInt(f * m, 10) / m
}
if (!property) {
return arr.reduce((a, b) => formatFloat(toNumber(a) + toNumber(b)), 0)
}
return arr.reduce(
(total, item) => formatFloat(toNumber(total) + toNumber(item[property])),
0
)
}
/**
* 数值类型格式化(这里不用toFixed,因为toFixed会存在进一位的情况)
* @param {*} num
* @param {*} fractionDigits
* @returns
*/
export function fixedNumber(_num, fractionDigits = 2) {
const num = String(_num)
// 处理非数字
if (!isNumber(num)) {
return Number(0).toFixed(fractionDigits)
}
// 小数点处理
let numStr = num.replace(/\.\d*$/g, function(m) {
return m.slice(0, fractionDigits + 1)
})
// 小数位不足,则补零
const amountAry = numStr.split('.')
if (amountAry.length > 1 && amountAry[1].length < fractionDigits) {
amountAry[1] = amountAry[1].padEnd(fractionDigits, '0')
} else if (amountAry.length < fractionDigits) {
amountAry[1] = '0'.repeat(fractionDigits)
}
numStr = fractionDigits < 1 ? amountAry[0] : amountAry.join('.')
return numStr
}
/**
* 金额格式化
* @param {*} amount
* @returns
*/
export function formatAmount(_amount, fractionDigits = 2) {
let amount = fixedNumber(_amount, fractionDigits)
// 处理非数字
if (!isNumber(amount)) {
return Number(0).toFixed(fractionDigits)
}
// 千分位处理
amount = !(amount + '').includes('.')
? // 1-3位后面一定要匹配3位
(amount + '').replace(/\d{1,3}(?=(\d{3})+$)/g, (match) => {
return match + ','
})
: (amount + '').replace(/\d{1,3}(?=(\d{3})+(\.))/g, (match) => {
return match + ','
})
// 小数点处理
return amount
}
实测(使用案例):
- 金额求和测试
let mockData = [{cost:0.01}, {cost:0.02}, {cost:9999.82}, {cost:6699.120090213}, {cost:6699.25}]
// --
console.log('数值求和: ', 0.01+0.02+9999.82+6699.120090213+6699.25)
console.log('reduce对象求和: ', mockData.reduce((total,item) => total+item.cost,0))
console.log('calcSum 函数测试 求和: ', calcSum(mockData,'cost'))
console.log('fixedNumber+calcSum 函数测试 求和: ', fixedNumber(calcSum(mockData,'cost')))
console.log('formatAmount+calcSum 函数测试 求和: ', formatAmount(calcSum(mockData,'cost')))
输出: