number.toFixed和Math.round与保留小数
如果你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与保留小数的更多相关文章
- Number(),parseInt(),parseFloat(),Math.round(),Math.floor(),Math.ceil()对比横评
首先,这些处理方法可分为三类. 1,只用来处理数字取整问题的:Math.round(),Math.floor(),Math.ceil(): 2,专门用于把字符串转化成数值:parseInt(),par ...
- WinCE的C#编程,对float型进行四舍五入保留两位小数,小数进行四舍五入操作,Math.Round的应用案例。
private float ConvertFloat4Se5Ru(float flotValue) { int iValue = (int)Math.Round(flotV ...
- 【JAVA】Math.Round()函数常见问题“四舍5入”
java.lang.Math.Round()使用时候,处理方式整理,方便以后查找 /** * 测试函数 2014-01-10 */ public class TestMath { pu ...
- (Math.round(num*100)/100).toFixed(2); 将输入的数字变成保留两位小数
<input type="number" @input="onInputPrice" @blur="onPrice" data-id= ...
- Js 和 PHP 中保留小数点后X位数的方法 toFixed、round、number_format、sprintf
在 Javacript 中保留小数点后两位数的方法为 toFixed(2),其中的2为保留两位,写多少就保留多少了,满5进1. Javacript例子: var num = 24.54789523; ...
- JAVA除法保留小数点后两位的两种方法 Java Math的 floor,round和ceil的总结
floor 返回不大于的最大整数 round 则是4舍5入的计算,入的时候是到大于它的整数round方法,它表示“四舍五入”,算法为Math.floor(x+0.5),即将原来的数字加上0.5后再向下 ...
- Js中toFixed()方法保留小数不精准的问题
toFixed() 方法可把 Number 四舍五入为指定小数位数的数字. 问题:部分特殊数值使用toFixed() 方法会出现转换不正确的情况,举个例子: (3329.225).toFixed(2) ...
- java.math.BigDecimal保留两位小数,保留小数,精确位数
http://blog.csdn.net/yuhua3272004/article/details/3075436 使用java.math.BigDecimal工具类实现 java保留两位小数问题 ...
- C#对应JavaScript的银行家舍入规则(Math.Round()对应toFixed(f))
Math.Round((n * u - t * u )/ u, f);//这里使用银行家四舍五入对应JS的 toFixed() ((n * u - t * u) / u).toFixed(f) f为小 ...
随机推荐
- js 注意事项使用误区
1.加法注意事项 2.浮点数注意事项 3.js,数组需使用数字作为下标索引,不支持关联数组的用法.对象不能混淆使用数组的length方法,并且不能使用数字作为下标,得使用属性值作为下标使用,否则会返回 ...
- 版本控制工具——Git常用操作
本文引自:https://juejin.im/post/5c206f1bf265da615114c15a
- cordova+vue 项目打包成APK应用遇到的问题和解决方法
公司前端界面用的是vue,我要嵌入到Android中生成App第一步:安装nodenode安装:直接进入官网https://nodejs.org/zh-cn/,下载最新版本安装.安装之后在命令行中使用 ...
- 【计算机网络】ISO/OSI 网络体系结构
ISO/OSI 网络体系结构 计算机网络是相当复杂的系统,相互通信的两个计算机系统必须高度协调才能正常工作.为了设计这样复杂的计算机网络,人们提出了将网络分层的方法.分层可将庞大而复杂的问题转换为若干 ...
- Linux命令详解 三
Linux用户和组的属性与权限本章结构1.用户账号和组账号概述2.用户账号和组账号管理3.查询账号信息4.查看目录或文件的属性5.设置目录或文件的权限6.设置目录或文件的归属 前言在linux中一切都 ...
- [Go] 轻量服务器框架基础TCP连接的抽象和封装
对tcp连接部分以及与连接绑定的业务部分进行抽象和封装 主要是对连接的开启关闭和读写进行封装,抽象出接口,使用回调进行具体业务的绑定 zinterface/iconnection.go package ...
- VS2017初学者如何打开右侧的解决方案资源管理器
- 给OPi Zero Plus添加USB启动功能
为使OPi Zero Plus支持U盘启动,需要在板载的SPI Flash当中刷入uboot.在这个过程当中绕了很多弯路,特此记录 最终操作步骤见文末 网上的教程仅使用sudo modprobe sp ...
- 一篇和Redis有关的锁和事务的文章
部分参考链接 Transaction StackExchange.Redis Transaction hashest 正文 Redis 是一种基于内存的单线程数据库.意味着所有的命令是一个接一个的执行 ...
- fjnu2019第二次友谊赛 B题
### 题目链接 ### 题目大意: 给你一个 n * m 的地图以及小蛇蛇头的初始位置,告诉你它会往 上.下.左.右 四个方向走.若在走的过程中(包括结束时)会使得小蛇越界,则输出 "Ga ...