小清新纯

小清新纯 查看完整档案

深圳编辑华南农业大学  |  信息管理与信息系统 编辑  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

小清新纯 关注了专栏 · 2019-01-10

SegmentFault 社区周刊

主题技术周刊,每周分享新鲜有趣的技术干货。

关注 1270

小清新纯 关注了用户 · 2019-01-10

keke @keke233

好好学习,天天向上~

关注 8403

小清新纯 评论了文章 · 2019-01-02

JS中如何理解浮点数?

本文由云+社区发表

相信大家在平常的 JavaScript 开发中,都有遇到过浮点数运算精度误差的问题,比如 console.log(0.1+0.2===0.3)// false。在 JavaScript 中,所有的数字包括整数和小数都是用 Number 类型来表示的。本文通过介绍 Number 的二进制存储标准来理解浮点数运算精度问题,和理解 Number 对象的 MAX_VALUE 等属性值是如何取值的,最后介绍了一些常用的浮点数精度运算解决方案。

Number 的存储标准

JavaScript Number 采用的是 IEEE 754 定义的 64 位双精度浮点型来表示。具体的字节分配可以先看一下引自维基百科的图:

img

从上图中可以看到,从高到低,64位被分成3段,分别是:

  • sign: 符号位,占 1 位;
  • exponent: 指数位,占 11 位;
  • fraction: 有效数字位,占 52 位。

指数位有 11 位,取值范围是 0 到 2047。当指数位 e=0 或者 e=2017 时,根据有效数字位 f 是否为 0 ,具有不同的特殊含义,具体见下表:

img

对于常用的 normal number, 为了方便表示指数为负数的情况,所以,指数位数值大小做了一个 -1023 的偏移量。对于一个非 0 数字而言,,它的二进制的科学计数法里的第一位有效数字固定是 1。这样,一个双精度浮点型数字的值就是

img

对于 subnormal number,它可以用来表示更加接近于 0 的数,它特殊的地方是有效数字位的前面补充的是 0 而不是 1,且指数为偏移量是 -1022,所以值是:

img

Number 对象中的几个属性值

知道了 Number 是如何存储之后,Number 对象的属性是如何取值的就明朗了。

Number.MAX_VALUE:可表示的最大的数,显然 e 和 f 都取最大时能表示的数最大,值为

img

Number.MIN_VALUE:可表示的最小的正数,用最小的 subnormal number 来表示。当 e = 0 ,f 的最后一位为 1,其他为 0 时最小,值为

img

Number.EPSILON : 表示 1 与 Number 可表示的大于 1 的最小的浮点数之间的差值。值为

img

Number.MAXSAFEINTEGER:表示在 JavaScript 中最大的安全整数。可以连续且精确被表示出来的整数成为安全整数,比如 2^54 就不是个安全整数,因为它和 2^54+1 两个数的表示是完全一样的,e=1077,f=0。 Math.pow(2,54)===Math.pow(2,54)+1// true。整数转化为二进制后,小数点后是不会有数字的,而用二进制的科学计数法表示时,小数点后最多保留 52 位,加上前置的一个 1,有 53 位数字,所以当一个数转化二进制时,如果位数超过 53 位,必然会截断末尾的部分,即导致不能精确表示,即为不安全整数。所以最小的会被截断的整数是 100...001=2^53+1(中间有52个0)。这个数设为 X,则比 X 小的整数都能被精确表示出来,再加上“连续”这个条件,所以 X-1 不是我们要的答案,X-2 才是。 Number.MAX_SAFE_INTEGER 最终值为

img

Number.MINSAFEINTEGER:表示在 JavaScript 中最小的安全整数,对 Number.MAX_SAFE_INTEGER 取负值即可,值为 -9007199254740991

为什么0.1+0.2不等于0.3

现在看看 console.log(0.1+0.2===0.3)// false 这个问题,数字 0.1 转化成二进制是 0.0001100110011... 即 1.10011001...1001 2^-4 (小数部分有52位,即有13个1001循环)。由于第 53 位是 1,类似 10 进制的四舍五入,二进制是“零舍一入”,所以 0.1 的最终二进制科学计数法表示是 1.10011001...1010 2^-4,即二进制数值大小实际上是 0.000110011001...10011010。下面的代码验证了这个值(打印出来的值,把最末尾的0去掉了):

var a = 0.1;console.log(a.toString(2)); //0.0001100110011001100110011001100110011001100110011001101

同理十进制数字 0.2 转化为二进制的最终值是 1.10011001...1010 2^-3 即 0.00110011...100111010;十进制 0.3 转化位二进制的最终值是 1.00110011...0011 2^-2

var b = 0.2;console.log(b.toString(2)); //0.001100110011001100110011001100110011001100110011001101var c = 0.3;console.log(c.toString(2)); //0.010011001100110011001100110011001100110011001100110011

所以,0.1+0.2 的值即为上面 0.1 和 0.2 对应的二进制数值的相加,如下图所示

img

上图中,对所得的和,“零舍一入”保留 52 位有效小数就是最终的值:0.01001100...110100(第 53 位是 1 ,所以往前进了 1),如下代码所示。这个值与上文中的 0.3 的最终二进制表示的值明显不相同,即解释了 0.1 + 0.2 不等于 0.3 的根本原因所在(实际上,这个值转化为 10 进制约等于 0.30000000000000004)。注:打印出来的长度是 54,因为有 52 位有效小数,前面是'0.01',长度是 4,最后去掉末尾的 2 个 0,所以最后打印出来的长度是 52+4-2 = 54。

var d = 0.1 + 0.2;console.log(d.toString(2)); //0.0100110011001100110011001100110011001100110011001101console.log(d.toString(2).length); // 54

浮点数精度运算解决方案

关于 js 浮点数运算精度丢失的问题,不同场景可以有不同的解决方案。 1、如果只是用来展示一个浮点数的结果,则可以借用 Number 对象的 toFixed 和 parseFloat 方法。下面代码片段中,fixed 参数表示要保留几位小数,可以根据实际场景调整精度。

function formatNum(num, fixed = 10) {    return parseFloat(a.toFixed(fixed))}var a = 0.1 + 0.2;console.log(formatNum(a)); //0.3

2、如果需要进行浮点数的加减乘除等运算,由上文可知,在小于 Number.MAXSAFEINTEGER 范围的整数是可以被精确表示出来的,所以可以先把小数转化为整数,运算得到结果后再转化为对应的小数。比如两个浮点数的加法:

 function add(num1, num2) {  var decimalLen1 = (num1.toString().split('.')[1] || '').length; //第一个参数的小数个数  var decimalLen2 = (num2.toString().split('.')[1] || '').length; //第二个参数的小数个数  var baseNum = Math.pow(10, Math.max(decimalLen1, decimalLen2));  return (num1 * baseNum + num2 * baseNum) / baseNum;}console.log(add(0.1 , 0.2)); //0.3

参考资料

此文已由作者授权腾讯云+社区发布


查看原文

小清新纯 回答了问题 · 2018-11-15

input:file 如何不通过点击触发。

<input type="file" id="upload">

var $input = document.querySelector('#upload');
$input.onclick = function() {
    console.log('a');
}
$input.click();

在chrome里面执行上面的代码,控制台会打印出console语句的内容,但是不会弹出选择文件的窗口,直接在控制台执行document.querySelector('#upload').click()就会弹出窗口。
在firefox会有一行提示信息说阻止了一个弹出窗口,如果选择了允许,就能弹出选择文件的窗口了。
这应该是浏览器出于安全方面的考虑吧。

关注 3 回答 2

小清新纯 回答了问题 · 2018-05-08

解决map循环嵌套,总是取得外层数组的最后一个值

map的返回值是一个数组,这个问题用forEach比较合适

let newArr = [];
arr01.forEach(a1 => {
    arr02.forEach(a2 => {
        newArr.push({
            account: a1.account,
            city: a2.city
        });
    });
});

关注 5 回答 4

小清新纯 回答了问题 · 2017-12-12

解决如何保持两个项目的同步登录?

单点登录(SSO,Single Sign On),也就是说你要把登录逻辑抽离出来,成为一个独立的服务。

SSO需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,SSO认证中心验证用户的用户名密码没问题,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,向SSO验证通过后即授权完成,可以创建局部会话,局部会话登录方式与单系统的登录方式相同。

大致原理是这样,具体实现可以搜索下SSO相关的知识。

关注 4 回答 2

小清新纯 回答了问题 · 2017-12-12

解决iframe引用b站视频失败?

搜索 iframe跨域

关注 9 回答 6

小清新纯 回答了问题 · 2017-12-07

vue 获取高度的问题

既然是要 获取到数据填充完毕后容器的高度,那应该是在获取数据后的回调里获取窗口高度啊

关注 10 回答 7

小清新纯 回答了问题 · 2017-12-06

解决JS变量在页面刷新后没有销毁

namewindow对象的一个属性,window.name比较特殊,name值在不同的页面甚至不同域名加载后依旧存在,是解决跨域的一种方案。

关注 4 回答 3

小清新纯 回答了问题 · 2017-12-01

overflow-x设置后却只有竖向滚动怎么解决

因为4个.cus-imgItem占的宽度超过100%,就会被挤到下一行去了,这样就会出现纵向的滚动条了。
解决方法是在.cus-imgItem外再套一层div,这个div的宽度为4个.cus-imgItem的宽度和,然后overflow:hidden。但是这样.cus-imgItem的宽度就不能用百分比了。

<div class="cusImg">
    <div class="inner">
        <div class="cus-imgItem item1"></div>
        <div class="cus-imgItem item2"></div>
        <div class="cus-imgItem item3"></div>
        <div class="cus-imgItem item4"></div>
    </div>
</div>

.cusImg {
    width: 100%;
    height:300px;
    overflow-x: auto;
}
.inner {
    position: relative;
    width: 1680px;
    height: 100%;
    overflow: hidden;
}
.cusImg .cus-imgItem {
    float: left;
    width: 400px;
    height:300px;
    margin: 0 10px;
}

关注 5 回答 4

认证与成就

  • 获得 26 次点赞
  • 获得 18 枚徽章 获得 3 枚金徽章, 获得 4 枚银徽章, 获得 11 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2014-05-27
个人主页被 656 人浏览