如果你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. Axure制作dialog效果的动作步骤

    1.在Axure中添加一个弹框按钮  2.将动态面版拖动到界面中    3.双击动态面版,双击state  4.拖入一块图片占位符进来    5.设置图片  6.回到上一个界面设置动态面版的大小,使其 ...

  2. CentOS 安装nginx服务

    安装nginx服务 sudo yum install nginx 启动nginx systemctl start nginx 加入启动项 systemctl enable nginx 测试nginx服 ...

  3. java基础学习笔记 第二周(面向对象)

    Day01 什么是抽象数据类型:将不同数据类型的集合组成的一个整体,我们称为抽象数据类型 类就是一个抽象数据类型 成员变量:类中的数据类型就是成员变量(属性) 方法:类中的一些行为就是方法 面向过程( ...

  4. Android Studio 3.0+ Record Espresso Test 自动化测试

    准备工作 1.将android studio 版本升级到3.0+2.百度下载夜神模拟器 夜神模拟器的基本设置 PS:以上就是夜神模拟器的基本设置 Android Studio 连接夜神模拟器 //夜神 ...

  5. R-4 方差分析

    本节内容: 1:方差分析的原理 2:单因数方差分析 .双因数分析 3:交互项 一:方差分析是原理 方差分析原理 对总体均值的假设检验,有三种情况:1.总体均值与某个常数进行比较:2.两个总体均值之间的 ...

  6. 多个线程运行MR程序时hadoop出现的问题

    夜间多个任务同时并行,总有几个随机性有任务失败,查看日志: cat -n ads_channel.log |grep "Caused by" Caused by: java.uti ...

  7. 请求时发送OPTIONS请求

    最近在用uni-app开发项目时,发现一个之前没注意到的点,当我发送POST请求的时候,在NetWork可以看到在发送正式的POST请求时,会先发送一个OPTIONS请求,OPTIONS请求后才会发送 ...

  8. 一分钟了解Docker

    一.Docker概述 Docker和虚拟机一样,都拥有环境隔离的能力,但它比虚拟机更加轻量级,可以使资源更大化地得到应用. Docker用途:简单配置.代码流水线管理.开发效率.应用隔离.服务器整合. ...

  9. spring = servlet + 依赖管理 + 业务逻辑

    spring = servlet + 依赖管理 + 业务逻辑

  10. Scrapy框架-爬虫程序相关属性和方法汇总

    一.爬虫项目类相关属性 name:爬虫任务的名称 allowed_domains:允许访问的网站 start_urls: 如果没有指定url,就从该列表中读取url来生成第一个请求 custom_se ...