解决使用toFixed()四舍五入时精度不准的问题

问题:四舍五入保留三位小数时 精度不准确 如 num=3.3245 得到的结果却确3.324

原因解析:
在网上查了些资料才知道,
1、在JS内部所有的计算都是以二进制方式计算的。
2、JS内部无法无限制保存二进制数值的长度。(最长52位)

我们的计算机底层都是0和1,当然,计算机也不能保留无限长(无限大)的东西。知道了以上两点,应该就不难理解了为什么JS在计算超大的数值的时候,会出现问题了,那么为什么计算很小浮点数的时候也会出问题呢,因为在JS内部,浮点数也是用很长很长的二进制表示的。

解决办法:

一、自定义方法

function round(num,iCount) {
    
    

	// iCount 保留几位小数
	var srcValue = num;

	var zs = true;

	//判断是否是负数
	if ( srcValue < 0 )
	{
    
    
		srcValue = Math.abs(srcValue);
		zs = false;
	}

	var iB = Math.pow(10, iCount);

	//有时乘100结果也不精确
	var value1 = srcValue * iB;

	var anumber = new Array();
	var anumber1 = new Array();

	var fvalue = value1;
	var value2 = value1.toString();
	var idot = value2.indexOf(".");

	//如果是小数
	if ( idot != -1 )
	{
    
    
		anumber = srcValue.toString().split(".");

		//如果是科学计数法结果
		if ( anumber[1].indexOf("e") != -1 )
		{
    
    
			return Math.round(value1) / iB;
		}

		anumber1 = value2.split(".");
		
		if ( anumber[1].length <= iCount )
		{
    
    
			return parseFloat(num,10);
		} 

		var fvalue3 = parseInt(anumber[1].substring(iCount,iCount+1),10);
		
		if ( fvalue3 >= 5 )
		{
    
    
			fvalue = parseInt(anumber1[0],10) + 1;
		} 
		else 
		{
    
    
			//对于传入的形如111.834999999998 的处理(传入的计算结果就是错误的,应为111.835)
			if ( fvalue3 == 4 && anumber[1].length > 10 && parseInt(anumber[1].substring(iCount+1,iCount+2),10) == 9 )
			{
    
    
				fvalue = parseInt(anumber1[0],10) + 1;
			} 
			else 
			{
    
    
				fvalue = parseInt(anumber1[0],10);
			}
		}
	}
	//如果是负数就用0减四舍五入的绝对值
	if ( zs )
	{
    
    
		return fvalue / iB;
	} 
	else 
	{
    
    
		return 0 - fvalue / iB;
	}
}

调用 round(3.3245,3)//3.325

问题就得到解决了

二、原型上解决

Number.prototype.toFixed = function (n) {
    
    
  // n为期望保留的位数,超过限定,报错!
  if (n > 20 || n < 0) {
    
    
    throw new RangeError('toFixed() digits argument must be between 0 and 20');
  }
  // 获取数字
  const number = this;
  // 如果是NaN,或者数字过大,直接返回'NaN'或者类似'1e+21'的科学计数法字符串
  if (isNaN(number) || number >= Math.pow(10, 21)) {
    
    
    return number.toString();
  }
  // 默认保留整数
  if (typeof (n) == 'undefined' || n == 0) {
    
    
    return (Math.round(number)).toString();
  }

  // 先获取字符串
  let result = number.toString();
  // 获取小数部分
  const arr = result.split('.');

  // 整数的情况,直接在后面加上对应个数的0即可
  if (arr.length < 2) {
    
    
    result += '.';
    for (let i = 0; i < n; i += 1) {
    
    
      result += '0';
    }
    return result;
  }

  // 整数和小数
  const integer = arr[0];
  const decimal = arr[1];
  // 如果已经符合要求位数,直接返回
  if (decimal.length == n) {
    
    
      return result;
  }
  // 如果小于指定的位数,补上
  if (decimal.length < n) {
    
    
    for (let i = 0; i < n - decimal.length; i += 1) {
    
    
      result += '0';
    }
    return result;
  }
  // 如果到这里还没结束,说明原有小数位多于指定的n位
  // 先直接截取对应的位数
  result = integer + '.' + decimal.substr(0, n);
  // 获取后面的一位
 let last = decimal.substr(n, 1);
 if (/^\d(9){
    
    5,}[89]$/.test(decimal.substr(n))) {
    
    
    last += last + 1;
  }
  // 大于等于5统一进一位
  if (parseInt(last, 10) >= 5) {
    
    
    // 转换倍数,转换为整数后,再进行四舍五入
    const x = Math.pow(10, n);
    // 进一位后,转化还原为小数
    result = (Math.round((parseFloat(result) * x)) + 1) / x;
    // 再确认一遍
    result = result.toFixed(n);
  }

  return result;
};

如果对大家有起到帮助,可以点个赞

猜你喜欢

转载自blog.csdn.net/tq1711/article/details/109313261