JavaScript因为存在计算的精度问题,所以直接计算就可能会导致各种各样的bug,为了解决这个问题,就要使用BigNumber.js这个库。
BigNumber.js是一个用于任意精度计算的js库。可以在 官方文档 的console中测试使用。也可以通过 npm install bignumber.js --save 来安装。
然后 import BigNumber from ‘bignumber.js’ 来引入使用。他的大概原理是将所有数字当做字符串,重新实现了计算逻辑。缺点是性能比原生的差很多。
import BigNumber from 'bignumber.js'
// 获取小数位数
const getDecimalsLength = number => {
const matched = String(number || 0).match(/\.(\d+)$/)
return matched ? matched[1].length : 0
}
/**
* 格式化数字,每隔三位添加逗号
* @param {number} num 数值
* @returns {string} 格式化后的数值字符串
*/
export function parseNum(num) {
const numberString = (num || 0).toString()
// 在转换一些以万为单位的金额时,小数点位数会超过3位,小数点中的数字暂不解析
const decimalPointIndex = numberString.indexOf('.')
let intString = decimalPointIndex > -1 ? numberString.slice(0, decimalPointIndex) : numberString
let decimalString = decimalPointIndex > -1 ? numberString.slice(decimalPointIndex) : ''
let reg1 = new RegExp('(\\d)(?=(?:\\d{3})+($|\\.))','g')
return intString.replace(reg1, "$1,") + decimalString
}
/**
* 保留两位小数
* @param {number|string} number 保留两位小数
* @param {string} [mode=floor] 舍入模式,floor 是向下取整,round 是四舍五入,ceil 是向上进一位
*/
export const keep2Decimals = (number, mode = 'floor', decimals = 2) => {
const times = Math.pow(10, decimals)
const oneHundredTimes = new BigNumber(number).times(times)
// https://mikemcl.github.io/bignumber.js/#constructor-properties
const roundModes = {
ceil: BigNumber.ROUND_CEIL, // 向正无限大方向舍入,始终不会减少计算值
floor: BigNumber.ROUND_FLOOR, // 向负无限大方向舍入,始终不会增加计算值
round: BigNumber.ROUND_HALF_UP, // 四舍五入
}
const roundMode = roundModes[mode]
if (!Object.prototype.hasOwnProperty.call(roundModes, mode)) {
throw new Error(`暂不支持 ${mode} 舍入模式`)
}
const integer = oneHundredTimes.integerValue(roundMode)
const newNumber = new BigNumber(integer).dividedBy(times)
return newNumber.toNumber()
}
/** 将元为单位的金额转为以万为单位 */
export function yuan2wan(price, options = { digits : 6, parseNumber : false, mode : 'floor'}) {
let bigNumber = new BigNumber(price)
let { digits, parseNumber, mode } = options
if (digits === 4) {
bigNumber = new BigNumber(bigNumber.integerValue(BigNumber.ROUND_FLOOR))
} else if (digits === 6 && getDecimalsLength(bigNumber.toString()) > 2) {
bigNumber = new BigNumber(keep2Decimals(bigNumber,mode))
}
const result = bigNumber.dividedBy(1e4).toNumber()
if (!parseNumber) {
return result
}
return parseNum(result)
}
/** 将万为单位的金额转为以元为单位 */
export function wan2yuan(price, parseNumber = false) {
const bigNumber = new BigNumber(price)
const result = bigNumber.times(1e4).toNumber()
return parseNumber ? parseNum(result) : result
}
/** 将元为单位的金额转为以分为单位 */
export function yuan2fen(price, parseNumber = false) {
const bigNumber = new BigNumber(price)
const result = bigNumber.times(1e2).toNumber()
return parseNumber ? parseNum(result) : result
}
/** 将分为单位的金额转为以元为单位 */
export function fen2yuan(price, parseNumber = false) {
const bigNumber = new BigNumber(price)
const result = bigNumber.dividedBy(1e2).toNumber()
return parseNumber ? parseNum(result) : result
}
/**
* 计算两位的差额,可设置保留小数点位数
* @param {number} firstNumber 第一个数字
* @param {number} lastNumber 第二个数字
* @param {number} decimals 保留的小数点位数,默认为0
* @returns {number} 结果
*/
export function getDifference(firstNumber, lastNumber, decimals = 0) {
return Number(new BigNumber(firstNumber || 0)
.minus(lastNumber || 0)
.toFixed(decimals))
}
/**
* 金额大小写转换
* @export
* @param {any} m
* @returns {string} 转换后的大写金额
*/
export function NumberToChinese (m) {
var num = Number(m);
if(!/^\d*(\.\d*)?$/.test(num))throw(new Error(-1, "Number is wrong!"));
var AA = new Array("零","壹","贰","叁","肆","伍","陆","柒","捌","玖");
var BB = new Array("","拾","佰","仟","萬","億","圆","");
var CC = new Array("角", "分", "厘");
var a = (""+ num).replace(/(^0*)/g, "").split("."), k = 0, re = "";
for(var i=a[0].length-1; i>=0; i--){
switch(k){
case 0 : re = BB[7] + re; break;
case 4 : if(!new RegExp("0{4}\\d{"+ (a[0].length-i-1) +"}$").test(a[0]))
re = BB[4] + re; break;
case 8 : re = BB[5] + re; BB[7] = BB[5]; k = 0; break;
}
if(k%4 == 2 && a[0].charAt(i)=="0" && a[0].charAt(i+2) != "0") re = AA[0] + re;
if(a[0].charAt(i) != 0) re = AA[a[0].charAt(i)] + BB[k%4] + re; k++;
}
if(a.length>1){
re += BB[6];
for(var i=0; i<a[1].length; i++){
re += AA[a[1].charAt(i)] + CC[i];
if(i==2) break;
}
if(a[1].charAt(0)=="0" && a[1].charAt(1)=="0"){
re+="元整";
}
}else{
re+="元整";
}
return re;
}
/**
* 保留n位小数 不够失望添加0占位
* @param {number} value 数值 默认2位
* @returns {string} 格式化后的数值字符串
*/
export const formatnumber = (value, num = 2) => {
let a
let b
let c
let i
a = value.toString()
b = a.indexOf('.')
c = a.length
if (num === 0) {
if (b !== -1) {
a = a.substring(0, b)
}
} else {
// 如果没有小数点
// eslint-disable-next-line no-lonely-if
if (b === -1) {
a = `${a}.`
for (i = 1; i <= num; i++) {
a = `${a}0`
}
} else {
// 有小数点,超出位数自动截取,否则补0
a = a.substring(0, b + num + 1)
for (i = c; i <= b + num; i++) {
a = `${a}0`
}
}
}
return a
}
// 汉字金额
export const getChineseCharacterAmount = num => {
if (!num) return ''
if (isNaN(Number(num))) {
return ''
}
const sitToUnit = a => {
// eslint-disable-next-line no-magic-numbers
if (a > 10) {
// eslint-disable-next-line no-magic-numbers
a = a - 8
return (sitToUnit(a))
}
const zh = '分角圆拾佰仟万拾佰仟亿'
return zh.charAt(a)
}
let minus = ''
let mstr = ''
const text = num.toString()
const zh = '零壹贰叁肆伍陆柒捌玖'
if (text.indexOf('-') === 0) {
num = text.replace('-', '')
minus = '负'
}
let moneyNum = Number(num)
// eslint-disable-next-line no-magic-numbers
let money = Math.round(moneyNum * 100).toString(10)
const len = money.length
for (let i = 0; i < len; i++) {
mstr = mstr + zh.charAt(parseInt(money.charAt(i))) + sitToUnit(len - i - 1)
}
mstr = mstr.replace('零分', '')
mstr = mstr.replace('零角', '零')
let yy = 0
// eslint-disable-next-line no-constant-condition
while (true) {
let { length } = mstr
mstr = mstr.replace('零圆', '圆')
mstr = mstr.replace('零万', '万')
mstr = mstr.replace('零亿', '亿')
mstr = mstr.replace('零仟', '零')
mstr = mstr.replace('零佰', '零')
mstr = mstr.replace('零零', '零')
mstr = mstr.replace('零拾', '零')
mstr = mstr.replace('亿万', '亿零')
mstr = mstr.replace('万仟', '万零')
mstr = mstr.replace('仟佰', '仟零')
yy = mstr.length
if (yy === length) {
break
}
}
yy = mstr.length
if (mstr.charAt(yy - 1) === '零') {
mstr = mstr.substring(0, yy - 1)
}
yy = mstr.length
if (mstr.charAt(yy - 1) === '圆') {
mstr = `${mstr}整`
}
return minus + mstr
}