금액 처리: 천 분의 일 처리, 소수점 이하 소수점 이하 반올림만 가능합니다.
예: 0.1+0.2=0.1+0.2=0.30000000000000004 이런 종류의 문제에 대한 진행 상황을 어떻게 보장하나요?
서문
- 금액 데이터를 다룰 때는 '천 단위 구분'과 '소수점 이하 자릿수 유지'가 필요한 경우가 많은데, 단순히 toLocalString과 toFixed를 사용하면 몇 가지 문제가 발생하기 쉽습니다.
- 예를 들어, toLocalString은 일부 호환성 문제가 있으며 일부 하이브리드 프레임워크를 통해 모바일 앱을 개발할 때 유효하지 않을 수 있으며, toFixed는 반올림 방식이므로 한 자리 반올림이 가능합니다.
- 5를 2로 반올림하는 방식인 '5를 2로 반올림'은 5 뒤의 숫자에 따라 ① 5가 홀수일 때는 5를 1로 반올림하고, ② 5가 짝수일 때는 5가 들어가지 않는 두 가지 경우로 나눠서 ① 홀수 앞의 5는 5를 1로 반올림합니다.
- 금액 서식을 지정할 때 '천 단위 구분 기호' 외에 '소수점 이하 자릿수 유지'에 대한 요구 사항은 가장 가까운 소수점 이하를 반올림하지 않고 초과 소수점 이하를 직접 반올림하는 것입니다.
- 제가 제안하는 한 가지 방법은 다음과 같습니다.
코드가 여기 있습니다!
/**
* 값이 숫자 값인지 확인합니다.
* @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
)
}
/**
* 숫자 유형 서식
* @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)
})
// 소수점 이하 자릿수가 부족하면 0을 보충합니다.
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)
}
// 천분의 1초 처리
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')))
출력:





