解决精度丢失最常用的方法是将浮点数转成整数计算
/**
* 尝试将各种类型的输入转换为 number 类型
* @param value 值
* @param defaultValue 默认值
* @returns 返回value转成number类型后的数据
*/
export function toNumber(value: any, defaultValue: number = 0): number {
// 如果输入已经是数字,直接返回
if (typeof value === 'number') {
return value
}
// 尝试将字符串转换为数字
if (typeof value === 'string') {
const parsed = parseFloat(value)
return isNaN(parsed) ? defaultValue : parsed
}
// 将布尔值转换为数字(true -> 1, false -> 0)
if (typeof value === 'boolean') {
return value ? 1 : 0
}
return defaultValue
}
/**
* 重写四舍五入方法
* @param number {number} 数值
* @param places {number} 小数位数
* @returns 返回满足小数位数的数值
* 例如:
* 141.575 -> 141.58
* 2.0 -> 2
*/
export function toFixed(number: number, places: number = 2): string {
// 如果为NaN抛出异常
if (isNaN(number)) return ''
// 不需要小数位,直接四舍五入
if (places === 0) {
return Math.round(number).toString()
}
// 实际小数位数
const actualDecimal = number.toString().split('.')[1]
const actualDecimalLen = actualDecimal ? actualDecimal.length : 0
// 存在小数
let fixNum = ''
if (actualDecimalLen) {
// 求10的平方:实际小数位数
const actualDecimalN = Math.pow(10, actualDecimalLen)
// 求10的平方:实际小数位数与最终要求的小数位数差
const diffPlaces = Math.pow(10, actualDecimalLen - places)
// 求10的平方:最终要求的小数
const placesN = Math.pow(10, places)
// 数值进行四舍五入计算
// number * actualDecimalN 将小数全部转为整数 是为了避免精度丢失 141.575 * 100 会精度丢失
fixNum = (Math.round((number * actualDecimalN) / diffPlaces) / placesN).toString()
} else {
fixNum = number.toString()
}
// 小数位数不足时,补全
// if (places !== 0 && fixNum.indexOf('.') === -1) {
// fixNum += '.'
// }
// if (places !== 0) {
// while (fixNum.split('.')[1].length < places) {
// fixNum += '0'
// }
// }
return fixNum
}
/**
* 加法,解决精度丢失 0.1 + 0.2 = 0.30000000000000004
* @param num1 数值1
* @param num2 数值2
* @returns
* 例子: add(0.1, 0.2) 返回 0.3
*/
export const plus = (num1: number, num2: number) => {
let r1, r2
try {
r1 = num1.toString().split('.')[1].length
} catch (e) {
r1 = 0
}
try {
r2 = num2.toString().split('.')[1].length
} catch (e) {
r2 = 0
}
const m = Math.pow(10, Math.max(r1, r2))
return divide(Math.round(multiply(num1, m) + multiply(num2, m)), m)
}
/**
* 减法,解决精度丢失 0.3 - 0.1 = 0.19999999999999998
* @param num1 数值1
* @param num2 被减的数值
* @returns
* 例子: sub(0.3, 0.1) 返回 0.2
*/
export const subtract = (num1: number, num2: number) => {
return plus(num1, -num2)
}
/**
* 乘法,解决精度丢失 0.00035 * 100 = 0.034999999999999996
* @param num1 数值1
* @param num2 被减的数值
* @returns
* 例子: sub(0.00035, 100) 返回 0.035
*/
export const multiply = (num1: number, num2: number) => {
let m = 0
const s1 = num1.toString()
const s2 = num2.toString()
try {
m += s1.split('.')[1].length
} catch (e) {
//
}
try {
m += s2.split('.')[1].length
} catch (e) {
//
}
return (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) / Math.pow(10, m)
}
/**
* 除法,解决精度丢失 0.00035 * 100 = 0.034999999999999996
* @param num1 数值1
* @param num2 被减的数值
* @returns
* 例子: sub(0.00035, 100) 返回 0.035
*/
export const divide = (num1: number, num2: number) => {
let t1 = 0
let t2 = 0
try {
t1 = num1.toString().split('.')[1].length
} catch (e) {
//
}
try {
t2 = num2.toString().split('.')[1].length
} catch (e) {
//
}
const r1 = Number(num1.toString().replace('.', ''))
const r2 = Number(num2.toString().replace('.', ''))
return multiply(r1 / r2, Math.pow(10, t2 - t1))
}
/**
* 计算百分比,返回对应小数位数
* 公式(num1 / num2) * 100
* @param num1 {number}
* @param num2 {number}
* @param places {number} 小数位数
*/
export const toRate = (num1: number, num2: number, places: number = 0) => {
if (!num2 && num1) {
return toFixed(100, places)
}
return toFixed(multiply(divide(num1, num2), 100), places)
}