如果你baidu/google过或者自己写过保留两位小数,那下面这代码一定不陌生

Math.round(number*100)/100

那你使用过Number.prototype.toFixed这个方法吗。老实说此前我一次没用过,我猜我以前看书的时候没注意它(反省img...)。

今天看书复习再次看到这个方法,感觉很方便的,一个方法搞定保留小数,岂不是美滋滋。
研究以后发现事情并没有那么简单。

根据网上的说法,toFixed使用的是银行家舍入规则。并非我们熟悉的四舍五入,所以并不适合用来保留小数。对于银行家舍入的解释引用自互联网:

银行家舍入:所谓银行家舍入法,其实质是一种四舍六入五取偶(又称四舍六入五留双)法。
简单来说就是:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。

规则不难理解,可不知道为什么我就是想去浏览器控制台试试....

说好的银行家舍入呢(手动滑稽)。

到这里我开始自己对问题的猜想,是不是以前toFiexed舍入规则是这种银行家舍入规则,而后来随着版本的变更,方法已经变为我们熟知的四舍五入了。现在到底能不能用,我决定试试

function contrast(number,fractionDigits) {
var times = Math.pow(10,fractionDigits);
var roundNum = Math.round(number*times)/times;
var toFixedNum = number.toFixed(fractionDigits);
if(roundNum.toString() !== toFixedNum){
console.log('-------------------------');
console.count();
console.log("number:"+number);
console.log("fractionDigits:"+fractionDigits);
console.log("roundNum:"+roundNum);
console.log("toFixedNum:"+toFixedNum);
}
}
var number,
fractionDigits;
for (var i = 0; i < 1000; i++) {
number = Math.floor(Math.random()*Math.pow(10,16))/Math.pow(10,16);
//一开始是以toFiexed参数范围取的20位,后来发现精度只支持16位
fractionDigits = Math.floor(Math.random()*16);
contrast(number,fractionDigits)
}

实验的结果,有两种情况,一是值一样,保留的小数不同(末尾0舍去与否)

坏就坏在第二种情况

出问题的数,保留小数,都在15位,难道是支持的准确精度位数不够吗,并不是的,比如:

这图也可以证明,toFiexed并非什么银行家舍入规则,至少我现在使用的chrome 62.0.3202.75不是,我使用的6.10.3nodejs也不是

网上找不到答案,那就书上找呗。在犀牛书里3.1.4里找到了一个原因,二进制浮点数表示法:

此外在知乎看到一个相当典型的例子1.555+1

可见1.555,在js里,其实是一个非常接近真实的1.555,但小于它的一个近似值。

我们认为的那个该进一的5其实在计算机眼中是4999..由上面的结果,不难预见到下面的结果

由此可见真正的问题源于二进制浮点数表示法并不能精准表示十进制分数!

----------------------------------------------分割线----------------------------------------------

理论完了,下面说说应用。

虽然toFixed由于二进制浮点数表示法的精确问题,并不能成为可靠的保留小数方案。

但我注意到其对小数位数的保留比round实现的保留小数(未做补零处理前)位数准确,也就是实验中的第一种情况。

我想利用这一特点,来给round保留的小数补零!!

由于Math.round(number*times)/times正确处理过toFiexed可能出错的5(4999....),这时候再用toFiexed,就可以避免错误(因为现在是999....\000...)。而又可以利用toFiexed正确保留小数位数的特点来补零。

function contrast(number,fractionDigits) { 
var times = Math.pow(10, fractionDigits);
var roundNum = Math.round(number * times) / times;
var toFixedNum = number.toFixed(fractionDigits);
var decimal = roundNum.toString().split(".");
var realValue1 = roundNum.toString();//手动补0
var realValue2 = roundNum.toFixed(fractionDigits);//toFixed补0
if(decimal.length === 2 ){
if (decimal[1].length < fractionDigits) { 
realValue1 = decimal[0] + '.' + (decimal[1] + "0000000000000000").slice(0,fractionDigits); 
}
}else if(fractionDigits !== 0){
realValue1 = decimal[0] + '.' + ("0000000000000000").slice(0,fractionDigits);
}
if (realValue1 !== realValue2) {
console.log('-------------------------');
console.count('错误数');
console.log("number:" + number);
console.log("fractionDigits:" + fractionDigits);
console.log("roundNum:" + roundNum);
console.log("realValue1:" + realValue1);
console.log("realValue2:" + realValue2);
console.log("toFixedNum:" + toFixedNum);
}else{
console.count('正确数');
}
}
var number,
fractionDigits;
for (var i = 0; i < 10000; i++) {
number = Math.floor(Math.random()*Math.pow(10,20))/Math.pow(10,20);
fractionDigits = Math.floor(Math.random()*16);
contrast(number,fractionDigits)
}

实验了10000*10次随机数随机保留小数,无错误。

理论和实际都无问题,以后我保留小数就准备这样用了!

function toFixed(number,fractionDigits){
var times = Math.pow(10, fractionDigits);
var roundNum = Math.round(number * times) / times;
return roundNum.toFixed(fractionDigits);
}

number.toFixed和Math.round与保留小数的更多相关文章

  1. Number(),parseInt(),parseFloat(),Math.round(),Math.floor(),Math.ceil()对比横评

    首先,这些处理方法可分为三类. 1,只用来处理数字取整问题的:Math.round(),Math.floor(),Math.ceil(): 2,专门用于把字符串转化成数值:parseInt(),par ...

  2. WinCE的C#编程,对float型进行四舍五入保留两位小数,小数进行四舍五入操作,Math.Round的应用案例。

    private  float ConvertFloat4Se5Ru(float flotValue) {              int iValue = (int)Math.Round(flotV ...

  3. 【JAVA】Math.Round()函数常见问题“四舍5入”

    java.lang.Math.Round()使用时候,处理方式整理,方便以后查找   /**  * 测试函数 2014-01-10  */ public class TestMath {     pu ...

  4. (Math.round(num*100)/100).toFixed(2); 将输入的数字变成保留两位小数

    <input type="number" @input="onInputPrice" @blur="onPrice" data-id= ...

  5. Js 和 PHP 中保留小数点后X位数的方法 toFixed、round、number_format、sprintf

    在 Javacript 中保留小数点后两位数的方法为 toFixed(2),其中的2为保留两位,写多少就保留多少了,满5进1. Javacript例子: var num = 24.54789523; ...

  6. JAVA除法保留小数点后两位的两种方法 Java Math的 floor,round和ceil的总结

    floor 返回不大于的最大整数 round 则是4舍5入的计算,入的时候是到大于它的整数round方法,它表示“四舍五入”,算法为Math.floor(x+0.5),即将原来的数字加上0.5后再向下 ...

  7. Js中toFixed()方法保留小数不精准的问题

    toFixed() 方法可把 Number 四舍五入为指定小数位数的数字. 问题:部分特殊数值使用toFixed() 方法会出现转换不正确的情况,举个例子: (3329.225).toFixed(2) ...

  8. java.math.BigDecimal保留两位小数,保留小数,精确位数

    http://blog.csdn.net/yuhua3272004/article/details/3075436 使用java.math.BigDecimal工具类实现   java保留两位小数问题 ...

  9. C#对应JavaScript的银行家舍入规则(Math.Round()对应toFixed(f))

    Math.Round((n * u - t * u )/ u, f);//这里使用银行家四舍五入对应JS的 toFixed() ((n * u - t * u) / u).toFixed(f) f为小 ...

随机推荐

  1. node-sass 埋坑记录

    node-sass 埋坑记录 背景 原有项目.环境: node:v8.16.2 npm:v6.4.1 node-sass::v4.8.0 Angular-CLI:v6.x 本机没有安装 Visual ...

  2. Flutter 快速上手定时器/倒计时及实战讲解

    本文微信公众号「AndroidTraveler」首发. 今天给大家讲讲 Flutter 里面定时器/倒计时的实现. 一般有两种场景: 我只需要你在指定时间结束后回调告诉我.回调只需要一次. 我需要你在 ...

  3. http模块

    1.引入http模块 const http = require('http') 2.创建node服务器 在创建node服务器的时候需要使用http模块中的http.creatServer()方法来进行 ...

  4. sql语句中给列参数取别名及相关注意事项

    1.使用双引号 select count(*) "总数" from table: 2.使用单引号 select count(*) '总数' from table: 3.直接加别名, ...

  5. Mac中创建一个OpenGL项目

    配置: OS X10.10 + Xcode 6.0 一.基本步骤 新建一个命令窗口项目 更改目标设备版本号为 OS X 8.0 添加库文件 GLUT.framework 和 OpenGL.framew ...

  6. Python time.time()方法

    描述 Python time time()返回当前时间的时间戳. 时间戳:时间戳是指格林威治时间1970年01月01日00时00分00秒(背景时间1970年01月01日08时00分00秒)起至现在的总 ...

  7. 学习强国docker文件用法

    学习强国docker用法 docker文件地址   https://github.com/fuck-xuexiqiangguo/docker 构建  docker  docker build -t D ...

  8. 查看Oracle被锁的表以及如何解锁

    注意权限问题 1.查看是否有被锁的表: select b.owner,b.object_name,a.session_id,a.locked_modefrom v$locked_object a,db ...

  9. [Abp vNext 源码分析] - 5. DDD 的领域层支持(仓储、实体、值对象)

    一.简要介绍 ABP vNext 框架本身就是围绕着 DDD 理念进行设计的,所以在 DDD 里面我们能够见到的实体.仓储.值对象.领域服务,ABP vNext 框架都为我们进行了实现,这些基础设施都 ...

  10. Selenium(十五):unittest单元测试框架(一) 初识unittest

    1. 认识unittest 什么是单元测试?单元测试负责对最小的软件设计单元(模块)进行验证,它使用软件设计文档中对模块的描述作为指南,对重要的程序分支进行测试以发现模块中的错误.在python语言下 ...