Math
的新特性 Number
属性Math
方法Number.parseInt()
和新的整数字面量Number
属性Number.EPSILON
Number.isInteger(number)
Math
功能您现在可以使用二进制和八进制表示法指定整数
> 0xFF // ES5: hexadecimal
255
> 0b11 // ES6: binary
3
> 0o10 // ES6: octal
8
Number
属性 全局对象 Number
获得了一些新的属性
Number.EPSILON
用于比较浮点数时考虑舍入误差的容差。Number.isInteger(num)
检查 num
是否为整数(没有小数部分的数字)
> Number.isInteger(1.05)
false
> Number.isInteger(1)
true
> Number.isInteger(-3.1)
false
> Number.isInteger(-3)
true
Number.isSafeInteger(number)
Number.MIN_SAFE_INTEGER
Number.MAX_SAFE_INTEGER
Number.isNaN(num)
检查 num
是否为 NaN
值。与全局函数 isNaN()
相比,它不会将其参数强制转换为数字,因此对于非数字更安全
> isNaN('???')
true
> Number.isNaN('???')
false
Number
的另外三个方法与其同名的全局函数基本相同:Number.isFinite
、Number.parseFloat
、Number.parseInt
。Math
方法 全局对象 Math
具有用于数值、三角函数和位运算的新方法。让我们看四个例子。
Math.sign()
返回数字的符号
> Math.sign(-8)
-1
> Math.sign(0)
0
> Math.sign(3)
1
Math.trunc()
删除数字的小数部分
> Math.trunc(3.1)
3
> Math.trunc(3.9)
3
> Math.trunc(-3.1)
-3
> Math.trunc(-3.9)
-3
Math.log10()
计算以 10 为底的对数
> Math.log10(100)
2
Math.hypot()
计算其参数平方和的平方根(勾股定理)
> Math.hypot(3, 4)
5
ECMAScript 5 已经具有十六进制整数的字面量
> 0x9
9
> 0xA
10
> 0x10
16
> 0xFF
255
ECMAScript 6 带来了两种新的整数字面量
0b
或 0B
> 0b11
3
> 0b100
4
0o
或 0O
(即零后跟大写字母 O;第一种变体更安全)
> 0o7
7
> 0o10
8
请记住,Number
方法 toString(radix)
可用于查看以 10 以外的进制表示的数字
> 255..toString(16)
'ff'
> 4..toString(2)
'100'
> 8..toString(8)
'10'
(双点是必需的,这样属性访问的点就不会与小数点混淆。)
在 Node.js 文件系统模块 中,有几个函数具有参数 mode
。其值用于通过从 Unix 继承的编码来指定文件权限
这意味着权限可以用 9 位表示(3 类用户,每类 3 个权限)
用户 | 组 | 所有 | |
---|---|---|---|
权限 | r, w, x | r, w, x | r, w, x |
位 | 8, 7, 6 | 5, 4, 3 | 2, 1, 0 |
单个用户类别的权限存储在 3 位中
位 | 权限 | 八进制数字 |
---|---|---|
000 | ––– | 0 |
001 | ––x | 1 |
010 | –w– | 2 |
011 | –wx | 3 |
100 | r–– | 4 |
101 | r–x | 5 |
110 | rw– | 6 |
111 | rwx | 7 |
这意味着八进制数是所有权限的紧凑表示形式,您只需要 3 位数字,每类用户一位数字。两个例子
Number.parseInt()
和新的整数字面量 Number.parseInt()
(与全局函数 parseInt()
相同)具有以下签名
Number
.
parseInt
(
string
,
radix
?
)
Number.parseInt()
:十六进制数字字面量 如果满足以下条件,则 Number.parseInt()
为十六进制字面量表示法提供特殊支持 – 如果
radix
缺失或为 0,则删除 string
的前缀 0x
(或 0X
)。然后将 radix
设置为 16。通常,您永远不应省略 radix
。radix
为 16。例如
> Number.parseInt('0xFF')
255
> Number.parseInt('0xFF', 0)
255
> Number.parseInt('0xFF', 16)
255
在所有其他情况下,仅解析数字直到第一个非数字字符
> Number.parseInt('0xFF', 10)
0
> Number.parseInt('0xFF', 17)
0
Number.parseInt()
:二进制和八进制数字字面量 但是,Number.parseInt()
不支持二进制或八进制字面量!
> Number.parseInt('0b111')
0
> Number.parseInt('0b111', 2)
0
> Number.parseInt('111', 2)
7
> Number.parseInt('0o10')
0
> Number.parseInt('0o10', 8)
0
> Number.parseInt('10', 8)
8
如果要解析这类字面量,则需要使用 Number()
> Number('0b111')
7
> Number('0o10')
8
只要没有特殊前缀并提供了参数 radix
,Number.parseInt()
就可以很好地处理具有不同进制的数字
> Number.parseInt('111', 2)
7
> Number.parseInt('10', 8)
8
Number
属性 本节介绍构造函数 Number
在 ECMAScript 6 中获得的新属性。
四个与数字相关的函数已经作为全局函数可用,并已作为方法添加到 Number
中:isFinite
和 isNaN
、parseFloat
和 parseInt
。它们的工作方式与其全局函数几乎相同,但 isFinite
和 isNaN
不再将其参数强制转换为数字,这对 isNaN
尤为重要。以下小节将解释所有详细信息。
Number.isFinite(number)
Number.isFinite(number)
确定 number
是否为实际数字(既不是 Infinity
也不是 -Infinity
也不是 NaN
)
> Number.isFinite(Infinity)
false
> Number.isFinite(-Infinity)
false
> Number.isFinite(NaN)
false
> Number.isFinite(123)
true
此方法的优点是它不会将其参数强制转换为数字(而全局函数会)
> Number.isFinite('123')
false
> isFinite('123')
true
Number.isNaN(number)
Number.isNaN(number)
检查 number
是否为 NaN
值。
ES5 中进行此检查的一种方法是通过 !==
> const x = NaN;
> x !== x
true
更具描述性的方法是通过全局函数 isNaN()
> const x = NaN;
> isNaN(x)
true
但是,此函数会将非数字强制转换为数字,如果结果为 NaN
,则返回 true
(这通常不是您想要的)
> isNaN('???')
true
新方法 Number.isNaN()
不会出现此问题,因为它不会将其参数强制转换为数字
> Number.isNaN('???')
false
Number.parseFloat
和 Number.parseInt
以下两种方法的工作方式与其同名的全局函数完全相同。为了完整起见,将它们添加到了 Number
中;现在所有与数字相关的函数都可以在那里使用。
Number.EPSILON
特别是在使用小数时,舍入误差在 JavaScript 中可能会成为一个问题3。例如,0.1 和 0.2 不能精确表示,如果您将它们相加并与 0.3(也不能精确表示)进行比较,您会注意到这一点。
> 0.1 + 0.2 === 0.3
false
Number.EPSILON
指定了比较浮点数时的合理误差范围。它提供了一种更好的比较浮点值的方法,如以下函数所示。
function
epsEqu
(
x
,
y
)
{
return
Math
.
abs
(
x
-
y
)
<
Number
.
EPSILON
;
}
console
.
log
(
epsEqu
(
0.1
+
0.2
,
0.3
));
// true
Number.isInteger(number)
JavaScript 只有浮点数(双精度)。因此,整数只是没有小数部分的浮点数。
如果 number
是数字并且没有小数部分,则 Number.isInteger(number)
返回 true
。
> Number.isInteger(-17)
true
> Number.isInteger(33)
true
> Number.isInteger(33.1)
false
> Number.isInteger('33')
false
> Number.isInteger(NaN)
false
> Number.isInteger(Infinity)
false
JavaScript 数字只有足够的存储空间来表示 53 位有符号整数。也就是说,范围为 −253 < *i* < 253 的整数 *i* 是*安全*的。稍后将解释这到底意味着什么。以下属性有助于确定 JavaScript 整数是否安全
Number.isSafeInteger(number)
Number.MIN_SAFE_INTEGER
Number.MAX_SAFE_INTEGER
*安全整数*的概念围绕着如何在 JavaScript 中表示数学整数。在 (−253, 253) 范围内(不包括上下限),JavaScript 整数是*安全*的:它们与它们表示的数学整数之间存在一对一的映射。
超出此范围,JavaScript 整数是*不安全*的:两个或多个数学整数表示为同一个 JavaScript 整数。例如,从 253 开始,JavaScript 只能表示每隔一个数学整数
> Math.pow(2, 53)
9007199254740992
> 9007199254740992
9007199254740992
> 9007199254740993
9007199254740992
> 9007199254740994
9007199254740994
> 9007199254740995
9007199254740996
> 9007199254740996
9007199254740996
> 9007199254740997
9007199254740996
因此,安全的 JavaScript 整数是明确表示单个数学整数的整数。
Number
属性 指定安全整数上下限的两个静态 Number
属性可以定义如下
Number
.
MAX_SAFE_INTEGER
=
Math
.
pow
(
2
,
53
)
-
1
;
Number
.
MIN_SAFE_INTEGER
=
-
Number
.
MAX_SAFE_INTEGER
;
Number.isSafeInteger()
确定 JavaScript 数字是否是安全整数,可以定义如下
Number
.
isSafeInteger
=
function
(
n
)
{
return
(
typeof
n
===
'number'
&&
Math
.
round
(
n
)
===
n
&&
Number
.
MIN_SAFE_INTEGER
<=
n
&&
n
<=
Number
.
MAX_SAFE_INTEGER
);
}
对于给定值 n
,此函数首先检查 n
是否为数字和整数。如果两项检查都成功,则如果 n
大于或等于 MIN_SAFE_INTEGER
且小于或等于 MAX_SAFE_INTEGER
,则 n
是安全的。
我们如何确保使用整数的计算结果是正确的?例如,以下结果显然是不正确的
> 9007199254740990 + 3
9007199254740992
我们有两个安全的操作数,但结果不安全
> Number.isSafeInteger(9007199254740990)
true
> Number.isSafeInteger(3)
true
> Number.isSafeInteger(9007199254740992)
false
以下结果也不正确
> 9007199254740995 - 10
9007199254740986
这次,结果是安全的,但其中一个操作数不是
> Number.isSafeInteger(9007199254740995)
false
> Number.isSafeInteger(10)
true
> Number.isSafeInteger(9007199254740986)
true
因此,仅当所有操作数和结果都是安全的情况下,应用整数运算符 op
的结果才能保证是正确的。更正式地说
isSafeInteger
(
a
)
&&
isSafeInteger
(
b
)
&&
isSafeInteger
(
a
op
b
)
意味着 a op b
是正确的结果。
Math
功能 全局对象 Math
在 ECMAScript 6 中有几个新方法。
Math.sign(x)
Math.sign(x)
返回
-1
如果 x
是负数(包括 -Infinity
)。0
如果 x
是零4。+1
如果 x
是正数(包括 Infinity
)。NaN
如果 x
是 NaN
或不是数字。例子
> Math.sign(-8)
-1
> Math.sign(3)
1
> Math.sign(0)
0
> Math.sign(NaN)
NaN
> Math.sign(-Infinity)
-1
> Math.sign(Infinity)
1
Math.trunc(x)
Math.trunc(x)
去除 x
的小数部分。 补充了其他舍入方法 Math.floor()
、Math.ceil()
和 Math.round()
。
> Math.trunc(3.1)
3
> Math.trunc(3.9)
3
> Math.trunc(-3.1)
-3
> Math.trunc(-3.9)
-3
您可以像这样实现 Math.trunc()
function
trunc
(
x
)
{
return
Math
.
sign
(
x
)
*
Math
.
floor
(
Math
.
abs
(
x
));
}
Math.cbrt(x)
Math.cbrt(x)
返回 x
的立方根 (∛x)。
> Math.cbrt(8)
2
如果一个小数位于零之后,则可以更精确地表示它。我将用十进制小数来演示这一点(JavaScript 的数字在内部以 2 为基数存储,但同样的推理也适用)。
以 10 为底的浮点数在内部表示为 *尾数* × 10指数。*尾数* 在小数点前有一位数字,*指数* 根据需要“移动”小数点。这意味着,如果将一个小数转换为内部表示形式,则点前的零会导致尾数小于点前的一。例如
就精度而言,这里重要的量是用有效数字衡量的尾数的容量。这就是为什么 (A) 比 (B) 提供更高的精度。
此外,JavaScript 以更高的精度表示接近零的数字(例如,小数)。
Math.expm1(x)
Math.expm1(x)
返回 Math.exp(x)-1
。Math.log1p()
的反函数。
因此,每当 Math.exp()
的结果接近 1 时,此方法都提供更高的精度。您可以在以下交互中看到两者之间的区别
> Math.expm1(1e-10)
1.00000000005e-10
> Math.exp(1e-10)-1
1.000000082740371e-10
前者是更好的结果,您可以使用任意精度浮点数(“bigfloats”)的库(例如 decimal.js)来验证
>
var
Decimal
=
require
(
'decimal.js'
)
.
config
(
{
precision
:
50
}
);
>
new
Decimal
(
1e-10
)
.
exp
()
.
minus
(
1
)
.
toString
()
'1.000000000050000000001666666666708333333e-10'
Math.log1p(x)
Math.log1p(x)
返回 Math.log(1 + x)
。Math.expm1()
的反函数。
因此,此方法允许您以更高的精度指定接近 1 的参数。以下示例演示了原因。
以下两次调用 log()
产生相同的结果
> Math.log(1 + 1e-16)
0
> Math.log(1 + 0)
0
相反,log1p()
产生不同的结果
> Math.log1p(1e-16)
1e-16
> Math.log1p(0)
0
Math.log1p()
精度更高的原因是 1 + 1e-16
的正确结果比 1e-16
具有更多有效数字
> 1 + 1e-16 === 1
true
> 1e-16 === 0
false
Math.log2(x)
Math.log2(x)
计算以 2 为底的对数。
> Math.log2(8)
3
Math.log10(x)
Math.log10(x)
计算以 10 为底的对数。
> Math.log10(100)
2
Emscripten 开创了一种后来被 asm.js 采用的编码风格:虚拟机(想想字节码)的操作在 JavaScript 的静态子集中表达。该子集可以由 JavaScript 引擎有效执行:如果它是 C++ 编译的结果,则其运行速度约为本机速度的 70%。
以下 Math
方法主要是为了支持 asm.js 和类似的编译策略而添加的,它们对其他应用程序没有那么有用。
Math.fround(x)
Math.fround(x)
将 x
舍入为 32 位浮点值 (float
)。由 asm.js 使用,用于告诉引擎在内部使用 float
值。
Math.imul(x, y)
Math.imul(x, y)
将两个 32 位整数 x
和 y
相乘,并返回结果的低 32 位。这是唯一不能通过使用 JavaScript 运算符并将结果强制转换回 32 位来模拟的 32 位基本数学运算。例如,idiv
可以实现如下
function
idiv
(
x
,
y
)
{
return
(
x
/
y
)
|
0
;
}
相反,将两个大的 32 位整数相乘可能会产生一个非常大的双精度数,从而丢失低位。
Math.clz32(x)
x
中的前导零位数。
> Math.clz32(0b01000000000000000000000000000000)
1
> Math.clz32(0b00100000000000000000000000000000)
2
> Math.clz32(2)
30
> Math.clz32(1)
31
为什么这很有趣?引用 Miro Samek 的“快速、确定和可移植的计数前导零”
计算整数中的前导零是许多 DSP 算法中的关键操作,例如声音或视频处理中样本的归一化,以及实时调度程序中快速找到准备运行的最高优先级任务。
Math.sinh(x)
x
的双曲正弦。Math.cosh(x)
x
的双曲余弦。Math.tanh(x)
x
的双曲正切。Math.asinh(x)
x
的反双曲正弦。Math.acosh(x)
x
的反双曲余弦。Math.atanh(x)
x
的反双曲正切。Math.hypot(...values)
> Math.hypot(3, 4)
5
JavaScript 的整数范围为 53 位。每当需要 64 位整数时,这都是一个问题。例如:在其 JSON API 中,当推文 ID 过大时,Twitter 不得不从整数切换到字符串。
目前,解决此限制的唯一方法是使用更高精度数字(bigint 或 bigfloat)的库。一个这样的库是 decimal.js。
在 JavaScript 中支持更大整数的计划已经存在,但可能需要一段时间才能实现。