🔢 JavaScript 数字类型
在 JavaScript 中,数字类型中两种:
- 常规数字以 64 位的格式 IEEE-754 (opens new window) 存储,也被称为「双精度浮点数」,安全范围在:-253 ~ 253 。常用的数字以这种为主。
- BigInt 数字,用于表示任意长度的整数。仅在少数特殊领域才会用到 BigInt 类型的数字。
# 数字的表示方法
# _
的使用
下划线可用作分隔符(是语法糖的角色),使得数字具有更强的可读性。JavaScript 引擎会直接忽略数字之间的 _
。
🌰 例子 / 下划线分割数字:
let billion = 1000000000; // before
let billion = 1_000_000_000; // after
2
# 科学计数法
在 JavaScript (许多编程语言中),使用
e
表示为 10 的几次方,由 e 后面跟着的定量数字决定,可以是正数也可以是负数。
🌰 例子:
let billion = 1e9; // 10 亿,字面意思:数字 1 后面跟 9 个 0
# 二进制、八进制、十六进制
二进制和八进制数字系统很少使用,但也支持使用 0b
和 0o
前缀:
let a = 0b11111111; // 二进制形式的 255
let b = 0o377; // 八进制形式的 255
alert( a == b );
2
3
4
十六进制数字在 JavaScript 中被广泛用于表示颜色,编码字符以及其他许多东西。 0x
,然后是数字。
alert( 0xff ); // 255
alert( 0xFF );
2
对于其他进制,应该使用函数 parseInt
。
# 与数字类型有关的方法
# toString(base)
使用此方法 num.toString(base)
返回在给定 base
进制数字系统中 num
的字符串表示形式(将二进制数字转换为相应的 base
进制,对应反过来的方法是 parseInt(str, radix)
。)。
base
的范围可以从 2
到 36
。默认情况下是 10
。
- base=16 用于十六进制颜色,字符编码等,数字可以是
0..9
或A..F
。- base=2 主要用于调试按位操作,数字可以是
0
或1
。- base=36 是最大进制,数字可以是
0..9
或A..Z
。所有拉丁字母都被用于了表示数字。
🌰 例子 :
let num = 255
console.log(num.toString(16)) // 转为十六进制
console.log(num.toString(2)) // 转为二进制
2
3
4
🌰 例子 / 将较长的数字使用 36 数字系统表示:
console.log(12345678..toString(36)) // 7clzi
..
两个点调用方法:想直接在一个数字上调用一个方法,比如上面例子中的toString
,那么需要在它后面放置两个点..
。这是因为 JavaScript 语法隐含了第一个点之后的部分为小数部分。如果我们再放一个点,那么 JavaScript 就知道小数部分为空,现在使用该方法。也可以使用括号分隔
(123456).toString(36)
# 数字的舍入
关于数字进行舍入的内建函数:
Math.floor
向下舍入。例如,3.1
变成3
,-1.1
变成-2
。Math.ceil
向上舍入。例如,3.1
变成4
,-1.1
变成-1
。Math.round
向最近的整数舍入,3.1
变成3
,3.6
变成4
,中间值3.5
变成4
。Math.trunc
移除小数点的所有内容(没有舍入)。例如,3.1
变成3
,-1.1
变成-1
。
常用的处理数字小数部分(移除)就是上述这些方法。
如果想要保留数字的小数部分(舍入到后 n
位),有两种方法:
toFixed(n)
:将数字舍入到小数点后n
位(规则四舍五入,向上或向下舍入到最接近的值),例如:let num = 12.34; num.toFixed(1); // 12.3
1
2let num = 12.36; num.toFixed(1); // 12.4
1
2toFixed
的结果是一个字符串。如果小数部分比所需要的短,则在结尾添加零(补上不足的小数位):let num = 12.34; alert( num.toFixed(5) ); // "12.34000"
1
2乘除法:例如,要将数字舍入到小数点后两位,我们可以将数字乘以
100
,调用舍入函数,然后再将其除回。例如:let num = 1.23456; Math.round(num * 100) / 100 // 1.23456 -> 123.456 -> 123 -> 1.23
1
2
# JavaScript 小数数字精度
JavaScript 中数字精度问题:
点击查看
在内部,数字是以 64 位格式 IEEE-754 (opens new window) 表示的,所以有 64 位可以存储一个数字:其中 52 位存储这些数字,11 位用于存储小数点的位置(对于整数,它们为零),而 1 位用于符号。
如果一个数字真的很大,则可能会溢出 64 位存储,变成一个特殊的数值 Infinity
:
console.log(1e500) // Infinity
🌰 例子 / 一个常见的小数加法运算,并不是预期的结果:
alert( 0.1 + 0.2 ); // 0.30000000000000004
在 JavaScript 中,一个数字以其二进制的形式存储在内存中,一个 1 和 0 的序列。但是在十进制数字系统中看起来很简单的 0.1
, 0.2
这样的小数,实际上在二进制形式中是无限循环小数。
IEEE-754 数字格式通过将数字舍入到最接近的可能数字来解决此问题。这些舍入规则通常不允许看到「极小的精度损失」,但是它确实存在。
alert( 0.1.toFixed(20) ); // 0.10000000000000000555
所以当对两个数字进行求和时,它们的「精度损失」会叠加起来。这就是为什么 0.1 + 0.2
不等于 0.3
。
解决方法:
- 使用
toFixed(n)
对结果舍入。
🌰 例子:
let sum = 0.1 + 0.2;
alert( sum.toFixed(2) ); // toFixed 总是返回一个字符串
alert( +sum.toFixed(2) ); // 使用一元加号将其强制转换为一个数字
2
3
- 将小数临时乘以小数位数的 10 的倍数,先将其转换为整数,进行数学运算,然后再除回相应的 10 的倍数。但是这种方法只可以减少误差,但不能完全消除误差。
🌰 例子:
alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001
2
小数的运算处理是不可能完全避免的。就算所有的价格数据都是整数,也避免不了打折计算结果的情况。所以最好的方法就是在得到计算小数的结果进行「剪掉尾巴」的处理,
提示
JavaScript 数字内部存在两个零, -0
和 0
,这是因为在存储时,使用一位来存储符号,因此对于包括零在内的任何数字,可以设置这一位或者不设置。在大多数情况下,这种区别并不明显,运算符将它们视为相同的值。
# isFinite
和 isNaN
对于两个特殊的数值: Infinity
( -Infinity
)(比任何数值都大的特殊数值)和 NaN
(计算出错的结果),它们属于 number
类型,但不是「普通」的数字,因此,这里有用于检查它们的特殊函数:
isNaN(value)
将其参数转换为数字,然后测试它是否为NaN
:console.log( isNaN(NaN) ); // true console.log( isNaN("str") ); // true
1
2注意:不能使用与
NaN
全等比较,NaN
独一无二的,它不等于任何东西,包括它自身。console.log(NaN === NaN)
1⭐️
isFinite(value)
将其参数转换为数字,如果是常规数字,则返回true
,而不是NaN/Infinity/-Infinity
,(可以理解为检验一个数字是否为有限值(不是NaN
或者Infinity
)):console.log(isFinite("15")) // true console.log(isFinite("str")) // true console.log(isFinite(Infinity)) // false
1
2
3还被用于验证字符串值是否为常规数字:
let num = +prompt("Enter a number", ''); console.log(isFinite(num));// 结果会是 true,除非你输入的是 Infinity、-Infinity 或不是数字
1
2
注意
在所有的数字方法函数中,包括 isInfinity
,空字符串或者仅有空格的字符串均被视为 0
。
提示
** 更可靠的比较方法 Object.is
**:这是一个特殊的 JavaScript 内建方法,它类似于 ===
一样对值进行比较,但它对于两种边缘情况更可靠:
NaN
:Object.is(NaN,NaN) === true
。- 值
0
和-0
是不同的:Object.is(0,-0) === false
。(从技术上看,数字的字符位可能会不同,这就导致数字并不相同,即使其他所有位都为零)
其他情况, Object.is(a,b)
完成的比较结果与 a === b
结果相同。
这种比较方式经常被用在 JavaScript 规范中。当内部算法需要比较两个值是否完全相同时,它使用 Object.is
(内部称为 SameValue (opens new window))。
提示
JavaScript 中检验用户的输入问题:现有一函数需要将用户输入的数值相加,返回相加后的结果。所以要判断用户的输入(完整考虑,不能仅仅只将用户的输入转换为数字):
function add(){
let a = +prompt('first num', 0)
if(!isFinite(a)) return alert('error')
let b = +prompt('second num', 0)
if(!isFinite(b)) return alert('error')
return alert(a + b)
}
2
3
4
5
6
7
8
9
10
# parseInt
和 parseFloat
前面所述,当我们需要将计算结果或者某一个值转换为数字类型,可以选择使用 +
或者 Number()
转换。使用这两种方法对于如果一个值不完全是数字(带有单位),就会失败:
console.log(+"100px") // NaN
但是对于字符串中存在空格(无论开头结尾),都会被忽略成功转换。所以这两种方法存在局限。
对于带有单位( '100px'
、 ‘12pt’
、 “19€”
)的数字字符串,要提取其中的数值,JavaScript 提供了 parseInt
和 parseFloat
,可以从字符串中读取出数字,直到无法读取:
parseInt
返回一个整数;parseFloat
返回一个浮点数;
🌰 例子:
console.log(parseInt('100px')) // 100
console.log(parseFloat('12.5em')) // 12.5
console.log(parseInt('12.5')) // 12 提取整数
console.log(parseFloat(3.14.159)) // 3.14 停止在第二个小数点
console.log(parseInt('a123')) // NaN 第一个符号即非数值,则停止读取,对于parseFloat也同理
2
3
4
5
6
7
提示
parseInt(str, radix)
的第二个参数: radix
指定了数字系统的基数,因此 parseInt
还可以解析十六进制数字、二进制数字等的字符串(转换为十进制整数):
console.log(parseInt('0xff', 16)) // 转换16进制的数为十进制
console.log(parseInt('ff', 16)) // 不带0x仍然有效
console.log(parseInt('2n9c', 36)) // 123456
2
3
4
# 其他数学函数
Math.random()
:返回一个从 0 到 1 的随机数(不包括 1。):console.log(Math.random()) // 0.5181640579695068
1与舍入的方法配合使用:
Math.floor(Math.random()*10) // 0~10之间的随机整数 Math.ceil(Math.random()*10) // 0~10之间的随机整数 Math.round(Math.random()*10) // *均衡获取0~10之间的随机整数
1
2
3
更详细的生成随机数的方案:
点击查看
生成从
min
到max
的随机整数:错误方案 / 边缘值的概率会比其他值少 :
function randomInteger(min, max) { let rand = min + Math.random() * (max - min); return Math.round(rand); }
1
2
3
4正确方案 / 调整取值范围的边界:
function randomInteger(min, max) { // 现在范围是从 (min-0.5) 到 (max+0.5) let rand = min - 0.5 + Math.random() * (max - min + 1); return Math.round(rand); }
1
2
3
4
5正确方案 / 取范围从
min
到max+1
的随机数 :function randomInteger(min, max) { let rand = min + Math.random() * (max + 1 - min); return Math.round(rand); }
1
2
3
4生成从
min
到max
的随机数:function random(min, max) { return min + Math.random() * (max - min) }
1
2
3
Math.max(a, b, c)
/Math.min(a, b, c)
:Math.max() - JavaScript | MDN (mozilla.org) (opens new window) 获取一组参数中(参数可以是一组数字,可以是数组)的最大值或者最小值:console.log(Math.max(1, 3, 2)); // expected output: 3 console.log(Math.max(-1, -3, -2)); // expected output: -1 const array1 = [1, 3, 2]; console.log(Math.max(...array1)); // expected output: 3 // 对于 Math.min() 使用方法同理。
1
2
3
4
5
6
7
8
9
10
11如果有任一参数不能转换为数值,则返回值为
NaN
。如果没有参数,结果为Infinity
(-Infinity
)。
提示
使用 Math.max()
或者 Math.min()
裁剪值:使某个方法的返回值始终小于或者大于某个值。
🌰 例子:
var x = f(foo);
if (x > boundary) {
x = boundary;
}
2
3
4
Copy to Clipboard
可以写成:
var x = Math.min(f(foo), boundary); // 只要返回值大于边界值就只返回边界值
# 数字类型总结
- 对于很多零(或者有很多小数位)的数字可以使用科学计数法
e
简化表示数值。 - 对于不同的数字系统(不同进制的数字),在前添加相关的前缀,十六进制(
0x
),八进制(0o
)和二进制(0b
),即可以在系统中写入数字。parseInt(str,base)
:指定的base
数字系统 (2 ≤ base ≤ 36
) 转换为十进制整数。num.toString(base)
:将数字转换为在给定的base
数字系统中的字符串(二进制转换为其他进制)。
- 提取出带有单位的数字字符串:使用
parseInt/parseFloat
进行转换,在特殊字符之前停止读取转换。 - 小数的舍入:使用
Math.floor
,Math.ceil
,Math.trunc
,Math.round
或num.toFixed(precision)
进行舍入。 - 使用小数会有损失数字精度的问题。
- 更多的数值类型的数学函数来自 Math (opens new window) 对象。