1、概述
JavaScript 不是类型语言。与许多其他编程语言不同,JavaScript 不定义不同类型的数字,比如整数、短、长、浮点等等。
JavaScript 中的所有数字都存储为根为 10 的 64 位(8 比特),浮点数。
所以在 JavaScript 内部,所有数字都是以64位浮点数形式储存,即使整数也是如此。例如:
- 1 === 1.0
- // true
-
- 1 + 1.0
- // 2
所以在 javascript 中,精确的整数(不使用小数点或指数计数法)最多为 16 位数字;精确的浮点数(包含整数和小数部分,不包含小数点)的最大位数也是 16位数字,例如:
- 1234567890123456
- // 1234567890123456(精确16位数字)
-
- 12345678901234567
- // 12345678901234568(只精确16位数字,多余的数字可能不准确)
-
- 12345678901234560
- // 12345678901234560(只精确16位数字,多余的数字可能不准确)
-
- 0.1234567890123456
- // 0.1234567890123456(精确16位数字)
-
- 1.123456789012345
- // 1.123456789012345(精确16位数字)
-
- 123.1234567890123
- // 123.1234567890123(精确16位数字)
-
- 123.12345678901233
- // 123.12345678901232(只精确16位数字,多余的数字可能不准确)
但是浮点运算并不总是 100% 准确,由于浮点数不是精确的值,所以涉及小数的比较和运算要特别小心,例:
- 0.1 + 0.2 === 0.3
- // 0.1+0.2 = 0.30000000000000004
- // false
-
- 0.3 / 0.1
- // 2.9999999999999996
根据国际标准IEEE 754,64位浮点数格式的64个二进制位中,第0位到第51位储存有效数字部分,第52到第62位储存指数部分,第63位是符号位,0表示正数,1表示负数。
因此,JavaScript提供的有效数字的精度为53个二进制位(IEEE 754规定有效数字第一位默认为1,再加上后面的52位),也就是说,小于2的53次方的整数都可以精确表示。
- Math.pow(2, 53) // 54个二进制位
- // 9007199254740992
-
- Math.pow(2, 53) + 1
- // 9007199254740992
-
- Math.pow(2, 53) + 2
- // 9007199254740994
-
- Math.pow(2, 53) + 3
- // 9007199254740996
-
- Math.pow(2, 53) + 4
- // 9007199254740996
从上面示例可以看到,大于2的53次方以后,整数运算的结果开始出现错误。所以,大于等于2的53次方的数值,都无法保持精度。
- Math.pow(2, 53)
- // 9007199254740992
-
- 9007199254740992111
- // 9007199254740992000
上面示例表明,大于2的53次方以后,多出来的有效数字(最后三位的111变成了000)都会无法保存,变成0。
另一方面,64位浮点数的指数部分的长度是11个二进制位,意味着指数部分的最大值是2047(2的11次方减1)。所以,JavaScript能够表示的数值范围为(-22048, 22048),超出这个范围的数无法表示。
2、表示及表示转换
数值可以字面形式直接表示,也可以采用科学计数法表示。以下两种情况,JavaScript会自动将数值转为科学计数法表示,其他情况都采用字面形式直接表示。
- 小数点前的数字多于21位。
- 小数点后的零多于5个。
如:
- 1234567890123456789012
- // 1.2345678901234568e+21
- // (整数部分超过16位数字,多余的数字将不精确
- // 整体超过21位数字,使用科学计数法表示)
-
- 123456789012345678901
- // 123456789012345678901
- // (整数部分超过16位数字,多余的数字将不精确
- // 但整体尚未超过21位数字,使用字面形式直接表示)
-
- 0.0000001
- // 1e-7
- // (小数部分开头超过5个连续0,使用科学计数法表示)
-
- 0.0001002
- // 0.0001002
- // (小数部分开头没有超过5个连续0,使用字面形式直接表示)
数值表示转换:
- // 转换为科学计数表示
- (123).toExponential(1);
- // "1.2e+2"
-
- (123).toExponential(5);
- // "1.23000e+2"
3、进制及进制转换
任何数值其实都可以转换为任何进制,其中常用的进制有十进制、二进制、八进制、十六进制,其中二进制、八进制、十六进制是应计算机而生的,因为他们都是2的N次幂。在表示不同进制时:
- 11
- // 十进制
- // 相当于十进制的11(1*10+1)
-
- 011
- // 八进制
- // 相当于十进制的9(1*8+1)
-
- 0x11
- // 十六进制
- // 相当于十进制的17(1*16+1)
进制转换:
- // 1、十进制转换其他进制
-
- // 十进制转换为二进制
- (123).toString(2);
- // "1111011"
-
- // 十进制转换为八进制
- (123).toString(8);
- // "173"
-
- // 十进制转换为十六进制
- (123).toString(16);
- // "7b"
-
- // 2、八进制转换其他进制
-
- // 八进制转换为二进制
- (0123).toString(2);
- // "1010011"
-
- // 八进制转换为十进制
- (0123).toString(10);
- // "83"
-
- // 八进制转换为十六进制
- (0123).toString(16);
- // "53"
-
- // 3、十六进制转换其他进制
-
- // 十六进制转换为二进制
- (0x123).toString(2);
- // "100100011"
-
- // 八进制转换为八进制
- (0x123).toString(8);
- // "443"
-
- // 八进制转换为十进制
- (0x123).toString(10);
- // "291"
4、parseInt与parseFloat
parseInt
是将数值或字符串解析为十进制数值的整数。parseFloat
是将数值或字符串解析为十进制浮点数。
parseInt
语法为:
- parseInt(string, radix);
- // string为待解析的字符串或数值
- // radix表示待解析的字符串或数值的进制
parseInt
有个误区,通常认为该方法是将字符串或数值转换为指定进制。比如:
- // => 错误认为会将十进制的10转换为八进制的12
- parseInt(10, 8);
parseInt没有转换进制的功能,它的字面意思是解析(parse)而不是转换(change),它只是将目标解析为十进制,所以:
- // 返回的结果不是8,而是NaN
- // 因为八进制当中没有8这个数值
- parseInt(8, 8);
parseInt的解析有以下特点:
A、会省略待解析的字符串的开头和结尾空格:
- parseInt(' 1 ');
- // 1
-
- parseInt('1');
- // 1
B、只保留从开头(不包含正负号)开始的连续数字,并会省略连续数字开头的0(但0后面为x较为特殊,表示十六进制):
- parseInt('2013年11月14日');
- // 2013
-
- parseInt('2013');
- // 2013
-
- parseInt('-99°C');
- // -99
-
- parseInt('-99');
- // -99
-
- parseInt('+99°C');
- // 99
-
- parseInt('+99');
- // 99
-
- parseInt('010-1234567');
- // 10
-
- parseInt('10-1234567');
- // 10
-
- parseInt('-010-1234567');
- // 10
-
- parseInt('-10-1234567');
- // 10
-
- parseInt('0a11');
- // 0
-
- parseInt('0x11');
- // 17(并不是0)
C、进制解析,可以解析[2,32]之间进制的数值为十进制:
- parseInt(011);
- // 9=1*8+1
-
- parseInt("011");
- // 11
-
- parseInt(11,8);
- // 9=1*8+1
-
- parseInt(0x11);
- // 17=1*16+1
-
- parseInt("0x11");
- // 17=parseInt(0x11)
-
- parseInt(11,16);
- // 17=1*16+1
parseInt的八进制和十六进制的字符串解析比较特殊,容易引起误会。
D、不符合以上规则的将解析为NaN:
- parseInt('云淡然');
- // NaN(开头没有数字)
-
- parseInt('云淡然2013');
- // NaN(数字不在开头)
-
- parseInt('--1');
- // NaN(正负号后面不是数字)
-
- parseInt(8, 8);
- // NaN(数字8不在八进制中)
parseFloat与parseInt的解析规则几乎一致,区别在于:
- A、parseFloat支持小数
- B、parseFloat符合概述和表示的规则
如:
- parseFloat(1234567890123456);
- // 1234567890123456
-
- parseFloat(12345678901234567);
- // 12345678901234568(不同,参考概述)
-
- parseFloat(123.1234567890123);
- // 123.1234567890123
-
- parseFloat(123.12345678901233);
- // 123.12345678901232(不同,参考概述)
-
- parseFloat(123456789012345678901);
- // 123456789012345678901
-
- parseFloat(1234567890123456789012);
- // 1.2345678901234568e+21(科学计数法,参考表示)
-
- parseFloat(0.0001002);
- // 0.0001002
-
- parseFloat(0.0000002);
- // 2e-7(科学计数法,参考表示)
5、Number
Number是将数字或字符串类型转换为数值对象,必要时两者之间可以自动转换。
- var a=1;
- var b=Number(a);
-
- a.toFixed(2);
- // 1.00
-
- b.toFixed(2);
- // 1.00
-
- a(数字基本类型)和b(数字对象类型)都有了对象属性,a自动转换为数字对象类型
和parseInt以及parseFloat类似,Number方法会先将参数解析,然后转换为数值对象。