15.数字

原文: http://exploringjs.com/impatient-js/ch_numbers.html

本章介绍 JavaScript 的数字单一类型,number

15.1。 JavaScript 只有浮点数

您可以在 JavaScript 中表示整数和浮点数:

但是,所有数字只有一种类型:它们都是 双倍 ,根据 IEEE 浮点运算标准(IEEE 754)实现的 64 位浮点数。

整数只是没有小数部分的浮点数:

请注意,在大多数情况下,大多数 JavaScript 引擎通常都能够使用实际整数,并具有所有相关的性能和存储大小优势。

15.2。数字字面值

让我们检查数字的字面值。

15.2.1。整数字面值

几个 整数字面值 让你用各种基数表示整数:

15.2.2。浮点字面值

浮点数只能用基数 10 表示。

分数:

指数:eN表示×10 N

15.2.3。语法缺陷:整数字面值的属性

访问整数字面值的属性会带来陷阱:如果整数字面值后面紧跟一个点,则该点被解释为小数点:

  1. 7.toString(); // syntax error

有四种方法可以解决这个陷阱:

15.3。号码运算符

15.3.1。二元算术运算符

%是余数运算符(不是模运算符) - 其结果具有第一个操作数的符号:

15.3.2。一元加和否定

两个运算符都将他们的操作数强制转换为数字:

15.3.3。递增(++)和递减(--

递增运算符++存在于前缀版本和后缀版本中。在这两个版本中,它破坏性地将一个添加到其操作数中。因此,其操作数必须是可以更改的存储位置。递减运算符--的工作方式相同,但从其操作数中减去一个。接下来的两个示例解释了前缀和后缀版本之间的区别。

前缀++和前缀--:更改然后返回。

后缀++和后缀--:返回然后更改。

15.3.3.1。操作数:不仅仅是变量

您还可以将这些运算符应用于属性值:

并且对于 Array 元素:

15.数字 - 图1 练习:数字运算符

exercises/numbers-math/is_odd_test.js

15.4。转换为数字

这些是将值转换为数字的三种方法:

  • Number(value)
  • +value
  • parseFloat(value)(避免;与其他两个不同!)

建议:使用描述性Number()

例子:

可以配置如何将对象转换为数字。例如,通过覆盖.valueOf()

15.数字 - 图2 练习:转换为数字

exercises/numbers-math/parse_number_test.js

15.5。错误值

发生错误时返回两个数字值:

  • NaN
  • Infinity

15.6。错误值:NaN

NaN是“非数字”的缩写。具有讽刺意味的是,JavaScript 认为它是一个数字:

什么时候NaN返回?

如果无法解析数字,则返回NaN

如果无法执行操作,则返回NaN

如果操作数或参数是NaN(传播错误),则返回NaN

15.6.1。检查NaN

NaN是唯一不严格等于自己的 JavaScript 值:

这些是检查值x是否为NaN的几种方法:

在最后一行,我们使用比较 quirk 来检测NaN

15.6.2。在数组中查找NaN

有些 Array 方法找不到NaN

其他人可以:

唉,没有简单的经验法则,你必须检查每个方法,它如何处理NaN

15.7。错误值:Infinity

何时返回错误值Infinity

如果数字太大,则返回无穷大:

如果除以零,则返回无穷大:

15.7.1。 Infinity作为默认值

Infinity大于所有其他数字(NaN除外),使其成为一个很好的默认值:

15.7.2。检查Infinity

这是检查值x是否为Infinity的两种常用方法:

15.数字 - 图3 练习:比较数字

exercises/numbers-math/find_max_test.js

15.8。数字的精确度:小心小数

在内部,JavaScript 浮点数用基数 2 表示(根据 IEEE 754 标准)。这意味着小数分数(基数 10)不能总是精确表示:

因此,在 JavaScript 中执行算术时需要考虑舍入误差。

继续阅读以解释这种现象。

15.数字 - 图4 测验:基本

参见测验应用程序

15.9。 (高级)

本章的所有其余部分都是高级的。

15.10。背景:浮点精度

在 JavaScript 中,使用数字进行计算并不总能产生精确的结果。例如:

要理解原因,我们需要探索 JavaScript 如何在内部表示浮点数。它使用三个整数来执行此操作,如 tbl 中所述。 5

Table 5: Internally, JavaScript uses three integers to represent floating point numbers. They take up a total of 64 bits of storage (double precision).

零件 尺寸 整数范围
标志 1 位 [0,1]
分数 52 位 [0,2 52 -1]
指数 11 位 [-1023,1024]

由这些整数表示的浮点数计算如下:

(-1)符号×0b1.fraction×2 指数

为了进一步讨论,我们简化了这种表示:

  • 我们使用基数 10(十进制)代替基数 2(二进制),因为这是大多数人更熟悉的。
  • 分数 是一个自然数,被解释为分数(一个点后的数字)。我们切换到 尾数 ,这是一个被解释为自身的整数。因此,指数的使用方式不同,但其基本作用不会改变。
  • 由于尾数是一个整数(有自己的符号),我们不再需要单独的符号了。

新表示的工作方式如下:

尾数×10 指数

让我们试试几个浮点数的表示。

  • 对于整数-123,我们主要需要尾数:

  • 对于数字 1.5,我们想象尾数后面有一个点。我们使用负指数将该点向左移动一位:

  • 对于数字 0.25,我们将点向左移动两位数:

具有负指数的表示也可以写为分母中具有正指数的分数:

这些分数有助于理解为什么我们的编码无法表示数字:

  • 可以表示1/10。它已经具有所需的格式:分母中的幂为 10。
  • 1/2可以表示为5/10。我们通过将分子和分母乘以 5,将分母中的 2 变为 10 的幂。
  • 1/4可以表示为25/100。通过将分子和分母乘以 25,我们将分母中的 4 变为 10 的幂。
  • 1/3无法表示。没有办法将分母变为 10 的幂。(10 的素数因子是 2 和 5.因此,只有这些素数因子的任何分母都可以通过乘以分子和分母来转换为 10 的幂。有足够的两个和五个。如果分母有不同的素因子,那么我们无能为力。)

为了结束游览,我们切换回基数 2:

  • 0.5 = 1/2可以用基数 2 表示,因为分母已经是 2 的幂。
  • 0.25 = 1/4可以用基数 2 表示,因为分母已经是 2 的幂。
  • 无法表示0.1 = 1/10,因为分母无法转换为 2 的幂。
  • 无法表示0.2 = 2/10,因为分母无法转换为 2 的幂。

现在我们可以看到为什么0.1 + 0.2不能产生精确的结果:在内部,两个操作数都不能精确表示。

精确计算小数分数的唯一方法是通过内部切换到基数 10.对于许多编程语言,base 2 是默认值,base 10 是一个选项。例如,Java 具有类 BigDecimal ,Python 具有模块 decimal 。暂时有计划添加类似于 JavaScript 的东西:ECMAScript 提案“Decimal”目前在阶段 0

15.11。 JavaScript 中的整数

JavaScript 没有特殊的整数类型。相反,它们只是没有小数部分的正常(浮点)数字:

在本节中,我们将介绍一些使用这些伪整数的工具。

15.11.1。转换为整数

将数字转换为整数的推荐方法是使用Math对象的一种舍入方法(在下一章中记录):

  • Math.floor(n):返回最大整数in

  • Math.ceil(n):返回最小整数in

  • Math.round(n):返回“最接近”n的整数。 0.5 向上舍入。例如:

  • Math.trunc(n):删除n具有的任何小数(在点之后),因此将其转换为整数。

TBL。 6 显示各种输入的这些功能的结果。

Table 6: Functions for converting numbers to integers. Note how things change with negative numbers, because “larger” always means “closer to positive infinity”.

-2.9 -2.5 -2.1 2.1 2.5 2.9
Math.floor -3 -3 -3 2 2 2
Math.ceil -2 -2 -2 3 3 3
Math.round -3 -2 -2 2 3 3
Math.trunc -2 -2 -2 2 2 2

15.11.2。 JavaScript 中的整数范围

这些是 JavaScript 中重要的整数范围:

  • 安全整数:可以通过 JavaScript“安全地”表示(更多关于接下来意味着什么)
    • 精度:53 位加号
    • 范围:( - 2 53 ,2 53
  • 数组索引
    • 精度:32 位,无符号
    • 范围:[0,2 32 -1)(不包括最大长度)
    • Typed Arrays 有更大的 53 位范围(安全和无符号)
  • 按位操作数(按位或等)
    • 精度:32 位
    • 无符号右移范围(>>>):无符号,[0,2 32
    • 所有其他按位运算符的范围:有符号,[ - 2 31 ,2 31

15.11.3。安全整数

这是 JavaScript 中 安全 的整数范围:

[-2 53 -1,2 53 -1]

如果一个整数是 安全 ,则它只由一个 JavaScript 数字表示。鉴于 JavaScript 数字被编码为一个小数乘以指数幂的 2,也可以表示更高的整数,但它们之间存在间隙。

例如(18014398509481984 是 2 54 ):

Number的以下属性有助于确定整数是否安全:

15.数字 - 图5 练习:检测安全整数

exercises/numbers-math/is_safe_integer_test.js

15.11.3.1。安全的计算

让我们看一下涉及不安全整数的计算。

以下结果不正确且不安全,即使它的两个操作数都是安全的。

以下结果是安全的,但不正确。第一个操作数是不安全的,第二个操作数是安全的。

因此,当且仅当两个操作数和结果都是安全的时,表达式a op b的结果才是正确的:

15.12。按位运算符

JavaScript 按位运算符的工作方式如下:

  • 首先,操作数转换为数字(64 位双浮点数),然后转换为 32 位整数。
  • 然后执行按位操作。
  • 最后,结果将转换回 double 并返回。

在内部,运算符使用以下整数范围(输入和输出):

  • 无符号右移运算符(>>>):32 位,范围[0,2 32
  • 所有其他按位运算符:32 位包括符号,范围[-2 31 ,2 31

如果我们以二进制表示法将它们显示为无符号 32 位整数,则其中一些运算符的结果最容易理解。这就是b32()的作用(其实现将在后面显示):

15.12.1。二进制按位运算符

Table 7: Binary bitwise operators.

手术 名称
num1 & num2 按位和
num1 | num2 按位或
num1 ^ num2 按位 Xor

二元运算符(tbl。 7 )组合其操作数的位以产生它们的结果:

15.12.2。按位不是

Table 8: The bitwise Not operator.

手术 名称
~num 按位不是,补充

按位 Not 运算符(tbl。 8 )反转其操作数的每个二进制数字:

15.12.3。按位移位运算符

Table 9: Bitwise shift operators.

手术 名称
num << count 左移
num >> count 签署右移
num >>> count 无符号右移

移位运算符(tbl。 9 )向左或向右移动二进制数字:

>>保留最高位,>>>不保留:

15.12.4。 b32():以二进制表示法显示 32 位整数

我们现在使用了b32()几次。以下代码是它的实现。

n >>> 0表示我们将n零位向右移位。因此,原则上,>>>运算符不执行任何操作,但它仍然将n强制转换为无符号的 32 位整数:

15.13。快速参考:数字

15.13.1。转换为数字

TBL。 10 显示如果通过Number()将各种值转换为数字会发生什么。

Table 10: Converting values to numbers.

x Number(x)
undefined NaN
null 0
布尔 false 0true 1
x(无变化)
'' 0
其他解析的数字,忽略前导/尾随空格
宾语 可配置的(例如通过.valueOf()

15.13.2。算术运算符

JavaScript 有以下算术运算符:

  • 二元算术运算符(tbl。 11
  • 前缀和后缀算术运算符(tbl。 12

Table 11: Binary arithmetic operators.

操作者 名称
n + m 加成 ES1 3 + 4 7
n - m 减法 ES1 9 - 1 8
n * m 乘法 ES1 3 * 2.25 6.75
n / m ES1 5.625 / 5 1.125
n % m 剩余 ES1 8 % 5 3
-8 % 5 -3
n ** m ES2016 4 ** 2 16

Table 12: Prefix and suffix arithmetic operators

操作者 名称
+n 一元加 ES1 +(-7) -7
-n 一元否定 ES1 -(-7) 7
v++ 增量 ES1 let v=0; [v++, v] [0, 1]
++v 增量 ES1 let v=0; [++v, v] [1, 1]
v-- 递减 ES1 let v=1; [v--, v] [1, 0]
--v 递减 ES1 let v=1; [--v, v] [0, 0]

15.13.3。按位运算符

JavaScript 具有以下按位运算符:

  • 按位和,或者,Xor,Not(tbl。 13
  • 按位移位运算符(tbl。 14

按位运算符的操作数和结果:

  • 无符号右移运算符(>>>):32 位,范围[0,2 32
  • 所有其他按位运算符:32 位包括符号,范围[-2 31 ,2 31

显示二进制数的辅助函数b32()在本章前面的中显示

Table 13: Bitwise And, Or, Xor, Not.

操作者 名称
i & j 按位和 ES1 (0b1010 & 0b1100).toString(2) '1000'
i &#124; j 按位或 ES1 (0b1010 &#124; 0b1100).toString(2) '1110'
i ^ j 按位 Xor ES1 (0b1010 ^ 0b0011).toString(2) '1001'
~i 按位不是 ES1 ~0b11111111111111111111111111111110 1

Table 14: Bitwise shift operators.

| 操作者 | 名称 | | 例 |
| —- | —- | —- | —- |
| i << j | 左移 | ES1 | (0b1 << 1).toString(2) '10' |
| i >> j | 签署右移 | ES1 | b32(0b10000000000000000000000000000010 >> 1) |
| | | | '11000000000000000000000000000001' |
| i >>> j | 无符号右移 | ES1 | b32(0b10000000000000000000000000000010 >>> 1) |
| | | | '01000000000000000000000000000001' |

15.13.4。数字的全局函数

JavaScript 具有以下四个全局函数:

  • isFinite()
  • isNaN()
  • parseFloat()
  • parseInt()

但是,最好使用Number的相应方法,这些方法具有较少的陷阱:Number.isFinite()Number.isNaN()Number.parseFloat()Number.parseInt()。它们是与 ES6 一起介绍的,将在下面讨论。

15.13.5。 Number的静态属性

  • .EPSILON: number [ES6]

    1 和下一个可表示的浮点数之间的差异。通常,机器 epsilon 为浮点运算中的舍入误差提供上限。

    • 大约:2.2204460492503130808472633361816×10 -16
  • .MAX_SAFE_INTEGER: number [ES6]

    JavaScript 可以明确表示的最大整数(2 53 -1)。

  • .MAX_VALUE: number [ES1]

    最大的正有限 JavaScript 数。

    • 大约:1.7976931348623157×10 308
  • .MIN_SAFE_INTEGER: number [ES6]

    JavaScript 可以明确表示的最小整数(-2 53 +1)。

  • .MIN_VALUE: number [ES1]

    最小的正面 JavaScript 数字。大约 5×10 -324

  • .NaN: number [ES1]

    与全局变量NaN相同。

  • .NEGATIVE_INFINITY: number [ES1]

    -Number.POSITIVE_INFINITY相同。

  • .POSITIVE_INFINITY: number [ES1]

    与全局变量Infinity相同。

15.13.6。 Number的静态方法

  • .isFinite(num: number): boolean [ES6]

    如果num是实际数字(Infinity-InfinityNaN),则返回true

  • .isInteger(num: number): boolean [ES6]

    如果num是一个数字并且没有小数部分,则返回true

  • .isNaN(num: number): boolean [ES6]

    如果num是值NaN,则返回true

  • .isSafeInteger(num: number): boolean [ES6]

    如果num是一个数字并且明确表示一个整数,则返回true

  • .parseFloat(str: string): number [ES6]

    将其参数强制转换为字符串并将其解析为浮点数。为了将字符串转换为数字,Number()(忽略前导和尾随空格)通常是比Number.parseFloat()更好的选择(它忽略前导空格和非法尾随字符并且可以隐藏问题)。

  • .parseInt(str: string, radix=10): number [ES6]

    将其参数强制转换为字符串并将其解析为整数,忽略前导空格和非法尾随字符:

    参数radix指定要解析的数字的基数:

    不要使用此方法将数字转换为整数:强制转换为字符串是低效的。并且在第一个非数字之前停止不是用于移除数字的分数的良好算法。这是一个出错的例子:

    最好使用Math的一个舍入函数将数字转换为整数:

15.13.7。 Number.prototype的方法

  • .toExponential(fractionDigits?: number): string [ES3]

    返回一个字符串,该字符串通过指数表示法表示数字。使用fractionDigits,您可以指定应显示与指数相乘的数字的位数(默认值是根据需要显示多少位数)。

    示例:数字太小而无法通过.toString()获得正指数。

    示例:分数不足以通过.toString()得到负指数。

  • .toFixed(fractionDigits=0): string [ES3]

    返回数字的无指数表示,舍入为fractionDigits个数字。

    如果数字为 10 21 或更高,则偶数.toFixed()使用指数:

  • .toPrecision(precision?: number): string [ES3]

    .toString()一样工作,但在返回结果之前将尾数修剪为precision数字。如果缺少precision,则使用.toString()

  • .toString(radix=10): string [ES1]

    返回数字的字符串表示形式。

    返回基数为 10 的结果:

    使用 10 以外的基数返回结果(通过radix指定):

    您可以使用parseInt()将整数结果转换回数字:

15.13.8。来源

15.数字 - 图6 测验:高级

参见测验应用程序