JavaSrcipt的数字(number):深入理解内部机制

Wesley13
• 阅读 913

一、数字的语法

JavaScript中的数字字面量一般用十进制表示。在JavaScript中表示数字的数据类型只有一种Number,这种天使与魔鬼同体的数据类型也就只有js了。

//同时表达整数和浮点数
var a = 78,
    b = 78.3;
console.log(typeof a);//number
console.log(typeof b);//number

然后还有一些奇葩的数字表示法:

var a = 0.42,
    b = .42;
if(a === b){
    console.log("你讲得对。")
}

一般情况下,奇葩都是成双成对的:

var a = 69.0,
    b = 69.;
if(a === b){
    console.log("你讲得对。")
}

这种奇葩你认识它就好了,最好不要出现在你的代码里,记得这种长得丑的写法不报错,但也没事别给自己挖坑。

var a = 89.900,
    b = 98.0;
console.log(a);//89.9
console.log(b);//98

小数点后面最后的面的零不管几个都省略,有时候发点神经,不拖泥带水还是挺潇洒的。然后还有指数计数格式计数与指数显示。

var a = 9E10;
console.log(a);//90000000000--通常我们使用指数计数法但是不会按照指数计数法显示
console.log(a.toExponential());//9e+10--通过toExponential方法转换成指数计数法字符串形式显示
var b = a * a;
console.log(b);//8.1e+21--当数字长度超出一定范围就会自动转成指数计数法
var c = 1 / b;
console.log(c);//1.234567901234568e-22--小写范围超出也会被转成指数计数法

这时候就有一个尴尬的问题来了,显示的是指数计数字面量,有多少人认识呢?所以就需要做数字化字面量处理:(下面的解决方案只能解决JavaScript整数安全范围内和最小小数范围内的数值。超出这个范围的数值听说可以解除Math.js,我也是在别人的博客里看到有介绍过的,没有亲测过。)

JavaSrcipt的数字(number):深入理解内部机制 JavaSrcipt的数字(number):深入理解内部机制

function toNonExponential(num) {
    var m = num.toExponential().match(/\d(?:\.(\d*))?e([+-]\d+)/);
    //通过match检索每个整个分组的值(非抓捕除外),并以数组表示
    //(m[1] || '').length;//--\d*--获取到小数位的长度
    //m[2];//--([+-]\d+)--获取到指数
    //(m[1] || '').length - m[2];小数位长度与指数的查(小数的长度)
    //小数位长度为负数时,设置为0--math.max取最大值
    return num.toFixed(Math.max(0, (m[1] || '').length - m[2]));
}
toNonExponential(3.3e-7)     // "0.00000033"
toNonExponential(3e-7)       // "0.0000003"
toNonExponential(1.401e10)   // "14010000000"
toNonExponential(0.0004)     // "0.0004"

View Code

指定小数部分的显示数位:Number.toFixed(x)

var a = 878.59;
a.toFixed(0);//"879"
a.toFixed(1);//"878.6"
a.toFixed(2);//"878.88"
a.toFixed(3);//"878.880"

关于toFixed方法会对数值做四舍五入处理,小数位不够时后面自动用0补齐,返回的值是字符串类型,参数x的取值范围是0~20。接下来看看有效数位处理方法toPrecision(x)

var a = 34.29;
a.toPrecision(1);//"3e+1"
a.toPrecision(2);//"34"
a.toPrecision(3);//"34.3"
a.toPrecision(4);//"34.29"
a.toPrecision(5);//"34.290"
//注意报错
34.toPrecision(3)//SyntaxError
34.toFixed(2)//SytaxError
(34).toPrecision(3);//34.0
(34).toFixed(2);//34.00

Number.toPrecision(x)方法也是会进行四舍五入操作,并且在数位无法表达数值的时候回进行科学计数法处理。值得注意的是toFixed(x)和toPrecision(x)这两个方法在遇到整数字面量调用时,需要用小括号将数值包裹起来,不然会报错。

最后就是一些特殊格式的数字字面量:

console.log(0xf3);//243--数字字面量十六进制
console.log(0363);//243--数字字面量八进制
//ES6支持的新格式
console.log(0o363);//243--ES6版本的数字字面量八进制
console.log(0b11110011);//234--ES6版本数字字面量二进制

二、很小的数字与JavaScript的数值精确问题

console.log(0.1 + 0.2 === 0.3);//false

从上面的代码我们可以看到一个违背了数学逻辑的问题,这是为什么呢?因为JavaScript所遵循IEEE754编程语言规范,这不只是JS的问题,而是所有遵循这一规范的编程语言都如此,也是通常说的二进制浮点数的精度缺陷,在这一精度下0.1+0.2的结果是0.30000000000000004,所以条件判断结果为false。

解决这一问题的办法就是设置一个误差范围,也就是通常说的“机器精度”,这个值通常是2^-52(2.220446049250313e-16)。在ES6中该值定义在Number.EPSILON中,在ES6的环境下我们可以直接拿来用,来版本环境可以通过计算获得这个值来使用:

if(!Number.EPSILON){
    Number.EPSILON = Math.pow(2,-52);
}
//这时候我们就可以来解决两个数字是否相等的问题了
function numbersCLoseEE(n1,n2){
    return Math.abs(n1 - n2) < Number.EPSILON;
}
var a = 0.1 + 0.2;
var b = 0.3;
numbersCLoseEE(a,b);

三、整数(范围、检测、32位有符号整数)

3.1JavaScript中整数最大可呈现2^53 - 1,即9007199254740991,在ES6中被定义为Number.MAX_SAFE_INTEGER。整数最小可呈现-(2^53 - 1),即-9007199254740991,在ES6中定义为Number.MIN_SAFE_INTEGER。超出这个范围的数字就只能采用字符串的方式来呈现了。

3.2关于检测一个数值是否是整数,可以使用ES6中的NumBer.isInteger(..)方法,老版本就只能自己做兼容处理:

Number.isInteger(42);        //true
Number.isInteger(42.00);    //true
Number.isInteger(42.3);        //false
//Number.isInteger(..)的兼容模式
if(!Number.isInteger){
    Number.isInteger = function(num){
        return typeof num == "number" && num % 1 == 0;
    }
}

关于检测一个数值是否是安全整数(即在最大范围和最小范围内的整数),可以使用ES6中的Number.isSafeInteger(...)方法,老版本做兼容处理:

if(!Number.isSafeInteger){
    Number.isSafeInteger = function(num){
        return Number.isInteger(num) && Math.abs(num) <= Number.MAX_SAFE_INTEGER;
    }
}

ES 6 增加了以下三个 Number 对象的属性:

  • EPSILON: 表示 1 和比最接近 1 且大于 1 的最小 Number 之间的差别
  • MIN_SAFE_INTEGER: 表示在 JavaScript中最小的安全的 integer 型数字 (-(253 - 1))。
  • MAX_SAFE_INTEGER: 表示在 JavaScript 中最大的安全整数(253 - 1)。

3.3JavaScript32位有符号整数:

虽然最大整数能够达到53位,但是有些数字操作只适应于32位数字,所以这些操作的数字范围只能在Math.pow(-2,31)到Math.pow(2,31)。

或运算符(“|”)只适应于32位以内的整数运算,所以有任意大小整数通过或运算符运算的话没有意义,比如任意大小整数a与0的运算,(a | 0)从运算范围来理解,这个运算本质上不存在任何意义。

四、特殊数字

1.不是数字的数字(NaN)

 数学运算时,操作数不是数字类型或者无法解析为常规的十进制或十六进制数字,就无法返回有效的数字,这种情况就会返回NaN。

var a = 2 / "foo";        //NaN
typeof a === "number";    //true

作为一个不是数字的Number类型的值,有一个特性是它不等于任何值,包括他自己(这家伙傻的可爱)。

var a = 2 / "foo";
a == NaN;     //false
a === NaN;     //false
//而且它很确定自己就不是自己
NaN != NaN; // true

既然无法进行比较,那如果在程序中我们由不得不比较它呢?全局对象window上有一个方法isNaN(...)可以用来判断它!

var a = 2 / "fuh";
window.isNaN(a);//true

但是,全局对象上的isNaN(...)并不靠谱,这个方法在检测值的时候,只要被判断的值不是数字就返回true。

var a = "aaa";
console.log(window.isNaN(a));//true

然后ES6在Number对象上添加了isNaN的方法解决了这个问题,但是老版本的浏览器就得要做兼容处理了:

if(!Number.isNaN){
    Number.isNaN = function(n){
        return (
            typeof n === "number" && 
            window.isNaN(n)
        );
    };
}
var a = 2 / "abc";
var b = "abc";
console.log(Number.isNaN(a));//true
console.log(Number.isNaN(b));//false

这个兼容的写法有点累赘,其实还有一个更简单的写法:

if(!Number.isNaN){
    Number.isNaN = function(n){
        return n !== n;
    }
}

2.无穷数

在JavaScript中除数是可以为0的,当1除以0时结果为Infinity(即Number.POSITIVE_INFINITY)。无穷数在JavaScript中也存在正无穷和负无穷,当一个负数除以0时,就可以得到-Infinity。

var a = 1 / 0;//Infinity
var a = -1 / 0;//-Infinity

因为JavaScript遵循IEEE754规范,在断定断定无穷数时存在向上和向下取值行为,我们知道在JavaScript中定义了最大值属性(Number.MAX_VALUE);但这个最大值并不代表再加上一个数就是无穷了,而且这个数跟一的差距还特别大,这种界定无穷的方法通俗来讲就是这个介于最大值和无穷之间的数更接近那一边。

console.log(Number.MAX_VALUE);//1.7976931348623157e+308
var a = Number.MAX_VALUE;
a + a;                //    Infinity
a + Math.pow(2,970);//    Infinity
a + Math.pow(2,969);//    1.7976931348623157e+308
//这部分不太必要过多研究,可能在你的工作中永远不会用到这个知识点,有兴趣的话作为学术讨论就好

要注意一点的是无穷除以无穷的结果为NaN。

3.零值

在JavaSrcipt中零是存在正负值的特殊数值,当零除以负数时得到的就是一个(-0),当然一个负数乘以零也是得到(-0);

var a = 0 / -5;//-0
var b = 0 * -6;//-0

在控制台打印时,这个结果并不一定准确,会因不同的浏览器和版本有不同的情况;还有一个情况就是在零值转换为字符串类型是就不会保留负值这个特性了,但是当带有负号的数字0的字符串转换成数字格式时又会保留负值。

var a = 0 / -9;    //-0
a.toString();      //"0"
a + "";            //"0"
String(a);         //"0"

+ "-0";            //-0
Number("-0");      //-0
JSON.parse("-0");  //-0

既然零值存在正负特性,但是在普通的逻辑运算看来0和-0是同一个东西,这两个东西是相等的。那么就会需要有判断正负的机制,写一个判断值为负零(-0)的方法:

function isNegZero(n){
    n = Number(n);
    return (n === 0) && (1 / n === -Infinity);
}
点赞
收藏
评论区
推荐文章
晴空闲云 晴空闲云
3年前
javascript实践教程-05-数据类型
本节目标1.掌握js中7种数据类型。2.掌握5种基本数据类型number、string、boolean、null、undefined的声明。3.掌握js中数组的声明和数组相关的方法。4.掌握js中对象的声明和属性、方法的使用。内容摘要本篇介绍了js中的7种数据类型,其中5种基本数据类型:number、string、boolean、null、unde
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Easter79 Easter79
3年前
typeScript数据类型
//布尔类型letisDone:booleanfalse;//数字类型所有数字都是浮点数numberletdecLiteral:number6;lethexLiteral:number0xf00d;letbinaryLiteral:number0b101
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
菜园前端 菜园前端
1年前
你了解JavaScript中的数据类型区分吗
原文链接:常见的ES5数据类型分为基本数据类型、引用数据类型两种。包含字符串、数字、对象、数组、函数、布尔值、空值、未知。基本数据类型String类型(字符串)javascriptvarname'xiaoming'Number类型(数字)javascrip
Wesley13 Wesley13
3年前
java 大数基本操作
导言:  计算机中数字的表示范围是有一定的限制的,像Java中,常用的数据类型,如int、double等数据类型表示的范围都是有限的,当我们要计算的数字,其位数达到成百上千时,这些数据类型无法满足我们的需求,C语言中我们可以使用数组来储存位数,再对两个数组进行相应的运算;Java中为了处理大整数的运算,提供了一种数据类型:BigInteger,BigDe
字节跳动最爱考的前端面试题:JavaScript 基础
​IT技术交易平台注意:每道题前面出现的(xx)数字代表这道题出现的频次,此JS基础是基于30篇前端面经整理出的问题和对应的回答、参考链接等。文章内容为拿到Offer的本人整理。(2)问:0.10.20.3嘛?为什么?JavaScript使用Number类型来表示数字(整数或浮点数),遵循IEEE754标准,通过
劳伦斯 劳伦斯
3年前
前端面试题自检 JS CSS 部分
JS类型JavaScript的简单数据类型Number,String,Boolean,Undefined,Null,Symboltypeof操作符的返回值numberstringbooleanundefinedobjectfunction
Easter79 Easter79
3年前
TypeScript 基本类型(一)
1、boolean布尔值true/falseletisDone:booleanfalse;2、number数字:和JavaScript一样,TypeScript里的所有数字都是浮点数。另外支持二进制,八进制,十进制,十六进制。letdecLiteral:number6;lethex
Stella981 Stella981
3年前
JavaScript(js)字面量,函数写法
JavaScript字面量在编程语言中,一般固定值称为字面量,如3.14。数字(Number)字面量可以是整数或者是小数,或者是科学计数(e)。3.141001123e5字符串(String)字面量可以使用单引号或双引号:"JohnDoe"'JohnDoe'表达式字面量用于计算: