Number 是什么
是一个使用双精度64位二进制格式(IEEE754)表示的 浮点值。
IEEE 754 双精度浮点数
组成
IEEE754双精度浮点数 在内存中使用 64 bits 来表示 3 个部分:
1.符号位(sign)
(1)占用 1 bit;
(2)符号位有 0 和 1 两个值:0 表示,为正数;1 表示,为负数。
2.指数(阶码)位(exponent)
(1)占用 11 bits;
(2)指数位可表示的最大值为 - 1 = 2047;最小值为 0 。
(3)内存中指数位的值并非真值,而是通过实际指数值和偏移量(中间值)相加而得。由于指数表示的是浮点数的小数 点位置,左移为正,右移为负。因此如果想在只有正数的情况下表示正负,则需要引入一个中间值作为实际值偏移的标准,也就是偏移量。通过取2047的中间值作为偏移量,偏移量 = ,这里 e 表示指数位数 ,得偏移量为1023。通过偏移量,指数位做到了表示正负指数。大于偏移量则为正数;小于偏移量则为负数。得指数真值的范围在 [ -1023,1024 ] 之间。
3. 尾数位(mantissa)
(1)占用 52 bits;
(2)表示 52 个有效数值。
内存存储如图所示:
计算
一个浮点数的表示方式有很多,但规范中一般使用科学计数法,例如在十进制中 1234 用,而不用或。二进制中只有0与1,那么按科学计数法,首位只可能是1,对此IEEE 754省略了这个默认的 1。对于首位为 1 的二进制科学计数法称为规格化。
规格化计算公式:
规格化浮点数的计算,即在进行计算前,一个隐藏的首位 1 被添加到尾数位mantissa上,这样可以使尾数的最高位始终为 1。
为了增加浮点数的表示能力,标准规定,在指数位exponent为 0 且mantissa不为 0 时,尾数位 mantissa 不再必须添加隐藏的首位 1,而是直接用二进制小数的形式表示,对应的浮点数值的计算公式变为:
非规格化计算公式:
根据指数位分出三种情况
(1) exponent 为 0
- 尾数mantissa为0:表示浮点0
- 尾数mantissa不为0:表示非规格化数 =
(2) exponent 为 2047
- 尾数mantissa为0:根据sign为0和1可以表示正负无穷大
- 尾数mantissa不为0:表示NaN(Not a Number)
(3) exponent 为 [1,2046]
使用规格化公式
正无穷数
=
负无穷数
=
最大正数
规格化最小正数
=
非规格化最小正数
=
最小负数
规格化最大负数
=
非规格化最大负数
=
Number 的值
+0,-0
由于JavaScript中的Number是根据IEEE 754格式存储在内存中,因此由于符号位sign的存在,0有+0和-0。因此本质上并不相同。
值的比较
// 在相等运算符下,0、-0、+0 视为值相等。
0 == +0 // true
0 == -0 // true
+0 == -0 // true
// 在严格相等运算符下,0、-0、+0 视为值相等。
0 === +0 // true
0 === -0 // true
+0 === -0 // true
// Object.is()静态方法下,+0 和 0视为相等,而-0不等于 +0 和 0
Object.is(0,+0) // true
Object.is(0,-0) // false
Object.is(+0,-0) // false
值的判断
// 0、+0 和 -0并非无法区分,由于负号的原因,可通过除法进行判断。
1 / 0 // Infinity
1 /-0 // -Infinity
NaN
全局属性NaN是一个表示非数字的值。根据IEEE754标准可知,NaN在内存中的表示并不唯一,这也解释了为什么严格相等总是不等于自身。
值的比较
const num = NaN;
// 在相等运算符下,NaN不等于任何值包括它自己。
num == num // false
// 在严格相等运算符下,NaN不等于任何值包括它自己。
num === num // false
// NaN无法判断大小
num > 0 // false
num < 0 // false
// Object.is()静态方法下,NaN等于NaN。
Object.is(num,num) // true
Object.is(num,NaN) // true
值的判断
// 通过isNaN()判断,输入参数会被进行强制转换成数字,如果符合IEEE754标准的NaN则返回true
isNaN(NaN); // true
isNaN('123a'); // true
isNaN({}); // true
isNaN(undefined) // true
isNaN(null) // false
isNaN(true); // false
isNaN([]); // false
isNaN('123'); // false
// 通过Number.isNaN()判断,该函数不会对参数进行强制转换,只判断参数是否为NaN
Number.isNaN(NaN); // true
Number.isNaN('123a'); // false
Number.isNaN({}); // false
Number.isNaN(undefined) // false
Number.isNaN(null) // false
Number.isNaN(true); // false
Number.isNaN([]); // false
Number.isNaN('123'); // false
Infinity
全局属性 Infinity 是一个数值,表示无穷大。有正无穷大和负无穷大之分,即±Infinity。根据IEEE754规定,无穷大 实际为 ,即JavaScript可表示数的上下限。
Number . MAX_SAFE_INTEGER
静态数据属性表示在 JavaScript 中最大的安全整数()。根据IEEE 754可知,数值在内存中最大有效位数为53位,最大安全整数由此而来。
Number . MIN_VALUE
JavaScript(IEEE754)可以表达的最小正数。
Number . MAX_VALUE
JavaScript(IEEE754)可以表达的最大正数。
进制数
JavaScript中默认使用十进制,但是也可以使用不同进制来表示数字,包括二进制、八进制和十六进制。但JavaScript对进制数有一些要求和限制。
// 二进制
0b111 // 7
0B111 // 7
// 八进制
0o17 // 15
0O17 // 15
017 // 15
// 十六进制
0x1a // 26
0X1a // 26
- JavaScript中默认使用十进制,因此使用非十进制时,如果不规定输出,进制则会自动转换为十进制。
console.log(0x1a); // 26
console.log(0x1a + 1); // 27
console.log(0x1a + '1') // 261
- 在使用十六进制时,JavaScript中的字母不区分大小写。例如,0xa和0xA是相同的。
0xa === 0xA // true
Object.is(0xa, 0xA) // true
- 在使用八进制时,如果使用数字8或9,它们将被视为十进制数字。因此,应该注意避免使用数字8和9。
0o19 // Uncaught SyntaxError: Invalid or unexpected token
019 // 19
转换为 Number 的方法
Number( )和 +
Number()将参数强制转换为数值原始值。如果值不能转换,则返回 NaN。
+号遵守和Number()强制转换完全相同的规则,只是+属于隐式转换。
// +号使用示例
+-1 // -1
+NaN // NaN
+{} // NaN
下文例子以Number()表示。
// number 类型
Number(-0) // -0
Number(+0) // 0
Number(NaN) // NaN
Number(Infinity) // Infinity
Number(-Infinity) // -Infinity
Number(0x1a) // 26
Number(070) // 56
// boolean 类型
Number(true) // 1
Number(false) // 0
// string 类型
Number('123') // 123
Number('123a') // NaN
Number('true') // NaN
Number('0x1a') // 26
Number('070') // 70
Number('0o70') // 56
Number('0b111') // 7
// undefined 和 null
Number(undefined) // NaN
Number(null) // 0
// 包装类型
Number(new Number()) // 0
Number(new Number(123)) // 123
Number(new Boolean()) // 0
Number(new Boolean(true)) // 1
Number(new String('123')) // 123
Number(new String('123a')) // NaN
// 对象
Number({}) // NaN
Number({toString(){return 1}}) // 1
Number({valueOf(){return 1}}) // 1
Number({toString(){return ''}}) // 0
Number({valueOf(){return ''}}) // 0
Number({toString(){return '123'}}) // 123
Number({valueOf(){return '123a'}}) // NaN
Number({valueOf(){return []}}) // NaN
Number({toString(){return []}}) // Cannot convert object to primitive value
Number({valueOf(){return new Number()}}) // NaN
Number({toString(){return new Number()}}) // Cannot convert object to primitive value
// 数组
Number([]) // 0
Number([123]) // 123
Number([123,123]) // NaN
Number()转换对象时遵循三个步骤:
- 将需要转化的对象进行 valueOf()操作,如果返回原始类型的值,就直接转化。
- 如果 valueOf()返回的是对象,则对其进行 toString()操作。如果toString() 返回原始类型的值,就直接转化。
- 如果 toString()返回的是对象,则报错 Cannot convert object to primitive value
parseInt( ) 和 Number.parseInt( )
parseInt() 和 Number.parseInt() 具有一样的函数功能。下文实例用parseInt()代表parseInt和Number.parseInt()。
parseInt === Number.parseInt // true
// 证明引用同一个函数,Number.parseInt() 是为了模块化的产物。
parseInt()更专注于字符串的整数解析,这是与Number()不同的地方。
Number('123a'); // NaN
parseInt('123a'); // 123
Number('a123'); // NaN
parseInt('a123'); // NaN
Number('1.23'); // 1.23
parseInt('1.23'); // 1
Number('0x1a'); // 26
parseInt('0x1a'); // 26
Number('0x1a'); // 26
parseInt('0x1a'); // 26
Number('070'); // 70
parseInt('070'); // 70 ES5规范不再允许parseInt函数的实现环境把以0字符开始的字符串作为八进制数值。
Number([]) // 0
parseInt([]) // NaN
// parseInt() 判断传入参数是否为字符串或数值,如果不是字符串返回NaN对字符串逐个字符解析,
// 如果第一个字符不是数字则直接返回NaN,
// 如果第一个是数字则解析到非数字为止
// 最后返回十进制数
parseInt()第二个参数可以指定转换的进制,范围在2 到 36 之间的整数 (10 + 26个英文字母)。
parseInt('070',2) // 0
parseInt('0x1a',2) // 0
// 在转换数值的时候,如果数值是二进制、八进制、十六进制,会先将其转换为十进制再转换为字符串进行逐个字符分析。
parseInt(1001,2) // 相当于parseInt(1001,2) 得 9
parseInt(070,2) // 相当于parseInt(56,2) 得 NaN
parseInt(012,2) // 相当于parseInt(10,2) 得 2
parseInt(070,8) // 相当于parseInt(56,8) 得 5*8+6 = 46
parseInt(0x1a,16) // 相当于parseInt(26,16) 得 2*16+6 = 38
parseInt(0x0c,2) // 相当于parseInt(12,2) 得 1
总结:
如果只有一个参数:parseInt(n)
-
n是数值,会被转换为10进制整数输出
-
n是字符串,判断是否以0x或0X开头:如果是,则从0x之后截取0-9,a-f的字符,最后根据十六进制输出十进制结果;如果不是,则截取0-9的字符输出。
如果有两个参数:parseInt(n,r)
-
如果n是数值,则会先转换为十进制整数,再转换为字符串,最后根据r的值逐个截取字符,将截取值转换为十进制整数。
-
如果n是字符串,则根据r的值逐个截取字符,将截取值转换为十进制整数。(一种情况特殊,如果以0x或者0X开头,当r为16时可以从0x之后开始截取0-9,a-f的字符)。
-
截取字符长度为0时输出NaN
parseFloat( ) 和 Number.parseFloat( )
parseFloat() 和 Number.parseFloat() 具有一样的函数功能。下文实例用parseFloat() 代表parseFloat 和 Number.parseFloat()。
parseFloat === Number.parseFloat // true
parseFloat()只有一个参数,专注于小数的字符串解析。
Number('1.23') // 1.23
parseFloat('1.23') // 1.23
Number('1.23a') // NaN
parseFloat('1.23a') // 1.23
// parseFloat无法识别十六进制
Number('0x1a') // 26
parseFloat('0x1a') // 0
关于 Number 的常见问题
为什么Number的原始值可以使用方法?
在访问数字的属性时,会创建一个Number类型的对象包装器,调用指定的方法,最后销毁了实例,而这一切行为都在JavaScript内部完成。
new Number() 和 Number() 的区别?
使用 new 时,Number将作为构造器使用,创建一个Number 对象,并不是原始值。 不使用new时,Number作为普通函数调用,它将参数强制转换为数值原始值。