JavaScript 數(shù)值

2023-03-20 15:57 更新

概述

整數(shù)和浮點(diǎn)數(shù)

JavaScript 內(nèi)部,所有數(shù)字都是以64位浮點(diǎn)數(shù)形式儲(chǔ)存,即使整數(shù)也是如此。所以,11.0是相同的,是同一個(gè)數(shù)。

1 === 1.0 // true

這就是說,JavaScript 語(yǔ)言的底層根本沒有整數(shù),所有數(shù)字都是小數(shù)(64位浮點(diǎn)數(shù))。容易造成混淆的是,某些運(yùn)算只有整數(shù)才能完成,此時(shí) JavaScript 會(huì)自動(dòng)把64位浮點(diǎn)數(shù),轉(zhuǎn)成32位整數(shù),然后再進(jìn)行運(yùn)算,參見《運(yùn)算符》一章的“位運(yùn)算”部分。

由于浮點(diǎn)數(shù)不是精確的值,所以涉及小數(shù)的比較和運(yùn)算要特別小心。

0.1 + 0.2 === 0.3
// false

0.3 / 0.1
// 2.9999999999999996

(0.3 - 0.2) === (0.2 - 0.1)
// false

數(shù)值精度

根據(jù)國(guó)際標(biāo)準(zhǔn) IEEE 754,JavaScript 浮點(diǎn)數(shù)的64個(gè)二進(jìn)制位,從最左邊開始,是這樣組成的。

  • 第1位:符號(hào)位,0表示正數(shù),1表示負(fù)數(shù)
  • 第2位到第12位(共11位):指數(shù)部分
  • 第13位到第64位(共52位):小數(shù)部分(即有效數(shù)字)

符號(hào)位決定了一個(gè)數(shù)的正負(fù),指數(shù)部分決定了數(shù)值的大小,小數(shù)部分決定了數(shù)值的精度。

指數(shù)部分一共有11個(gè)二進(jìn)制位,因此大小范圍就是0到2047。IEEE 754 規(guī)定,如果指數(shù)部分的值在0到2047之間(不含兩個(gè)端點(diǎn)),那么有效數(shù)字的第一位默認(rèn)總是1,不保存在64位浮點(diǎn)數(shù)之中。也就是說,有效數(shù)字這時(shí)總是1.xx...xx的形式,其中xx..xx的部分保存在64位浮點(diǎn)數(shù)之中,最長(zhǎng)可能為52位。因此,JavaScript 提供的有效數(shù)字最長(zhǎng)為53個(gè)二進(jìn)制位。

(-1)^符號(hào)位 * 1.xx...xx * 2^指數(shù)部分

上面公式是正常情況下(指數(shù)部分在0到2047之間),一個(gè)數(shù)在 JavaScript 內(nèi)部實(shí)際的表示形式。

精度最多只能到53個(gè)二進(jìn)制位,這意味著,絕對(duì)值小于2的53次方的整數(shù),即-253到253,都可以精確表示。

Math.pow(2, 53)
// 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次方以后,整數(shù)運(yùn)算的結(jié)果開始出現(xiàn)錯(cuò)誤。所以,大于2的53次方的數(shù)值,都無法保持精度。由于2的53次方是一個(gè)16位的十進(jìn)制數(shù)值,所以簡(jiǎn)單的法則就是,JavaScript 對(duì)15位的十進(jìn)制數(shù)都可以精確處理。

Math.pow(2, 53)
// 9007199254740992

// 多出的三個(gè)有效數(shù)字,將無法保存
9007199254740992111
// 9007199254740992000

上面示例表明,大于2的53次方以后,多出來的有效數(shù)字(最后三位的111)都會(huì)無法保存,變成0。

數(shù)值范圍

根據(jù)標(biāo)準(zhǔn),64位浮點(diǎn)數(shù)的指數(shù)部分的長(zhǎng)度是11個(gè)二進(jìn)制位,意味著指數(shù)部分的最大值是2047(2的11次方減1)。也就是說,64位浮點(diǎn)數(shù)的指數(shù)部分的值最大為2047,分出一半表示負(fù)數(shù),則 JavaScript 能夠表示的數(shù)值范圍為21024到2-1023(開區(qū)間),超出這個(gè)范圍的數(shù)無法表示。

如果一個(gè)數(shù)大于等于2的1024次方,那么就會(huì)發(fā)生“正向溢出”,即 JavaScript 無法表示這么大的數(shù),這時(shí)就會(huì)返回Infinity

Math.pow(2, 1024) // Infinity

如果一個(gè)數(shù)小于等于2的-1075次方(指數(shù)部分最小值-1023,再加上小數(shù)部分的52位),那么就會(huì)發(fā)生為“負(fù)向溢出”,即 JavaScript 無法表示這么小的數(shù),這時(shí)會(huì)直接返回0。

Math.pow(2, -1075) // 0

下面是一個(gè)實(shí)際的例子。

var x = 0.5;

for(var i = 0; i < 25; i++) {
  x = x * x;
}

x // 0

上面代碼中,對(duì)0.5連續(xù)做25次平方,由于最后結(jié)果太接近0,超出了可表示的范圍,JavaScript 就直接將其轉(zhuǎn)為0。

JavaScript 提供Number對(duì)象的MAX_VALUEMIN_VALUE屬性,返回可以表示的具體的最大值和最小值。

Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324

數(shù)值的表示法

JavaScript 的數(shù)值有多種表示方法,可以用字面形式直接表示,比如35(十進(jìn)制)和0xFF(十六進(jìn)制)。

數(shù)值也可以采用科學(xué)計(jì)數(shù)法表示,下面是幾個(gè)科學(xué)計(jì)數(shù)法的例子。

123e3 // 123000
123e-3 // 0.123
-3.1E+12
.1e-23

科學(xué)計(jì)數(shù)法允許字母eE的后面,跟著一個(gè)整數(shù),表示這個(gè)數(shù)值的指數(shù)部分。

以下兩種情況,JavaScript 會(huì)自動(dòng)將數(shù)值轉(zhuǎn)為科學(xué)計(jì)數(shù)法表示,其他情況都采用字面形式直接表示。

(1)小數(shù)點(diǎn)前的數(shù)字多于21位。

1234567890123456789012
// 1.2345678901234568e+21

123456789012345678901
// 123456789012345680000

(2)小數(shù)點(diǎn)后的零多于5個(gè)。

// 小數(shù)點(diǎn)后緊跟5個(gè)以上的零,
// 就自動(dòng)轉(zhuǎn)為科學(xué)計(jì)數(shù)法
0.0000003 // 3e-7

// 否則,就保持原來的字面形式
0.000003 // 0.000003

數(shù)值的進(jìn)制

使用字面量(literal)直接表示一個(gè)數(shù)值時(shí),JavaScript 對(duì)整數(shù)提供四種進(jìn)制的表示方法:十進(jìn)制、十六進(jìn)制、八進(jìn)制、二進(jìn)制。

  • 十進(jìn)制:沒有前導(dǎo)0的數(shù)值。
  • 八進(jìn)制:有前綴0o0O的數(shù)值,或者有前導(dǎo)0、且只用到0-7的八個(gè)阿拉伯?dāng)?shù)字的數(shù)值。
  • 十六進(jìn)制:有前綴0x0X的數(shù)值。
  • 二進(jìn)制:有前綴0b0B的數(shù)值。

默認(rèn)情況下,JavaScript 內(nèi)部會(huì)自動(dòng)將八進(jìn)制、十六進(jìn)制、二進(jìn)制轉(zhuǎn)為十進(jìn)制。下面是一些例子。

0xff // 255
0o377 // 255
0b11 // 3

如果八進(jìn)制、十六進(jìn)制、二進(jìn)制的數(shù)值里面,出現(xiàn)不屬于該進(jìn)制的數(shù)字,就會(huì)報(bào)錯(cuò)。

0xzz // 報(bào)錯(cuò)
0o88 // 報(bào)錯(cuò)
0b22 // 報(bào)錯(cuò)

上面代碼中,十六進(jìn)制出現(xiàn)了字母z、八進(jìn)制出現(xiàn)數(shù)字8、二進(jìn)制出現(xiàn)數(shù)字2,因此報(bào)錯(cuò)。

通常來說,有前導(dǎo)0的數(shù)值會(huì)被視為八進(jìn)制,但是如果前導(dǎo)0后面有數(shù)字89,則該數(shù)值被視為十進(jìn)制。

0888 // 888
0777 // 511

前導(dǎo)0表示八進(jìn)制,處理時(shí)很容易造成混亂。ES5 的嚴(yán)格模式和 ES6,已經(jīng)廢除了這種表示法,但是瀏覽器為了兼容以前的代碼,目前還繼續(xù)支持這種表示法。

特殊數(shù)值

JavaScript 提供了幾個(gè)特殊的數(shù)值。

正零和負(fù)零

前面說過,JavaScript 的64位浮點(diǎn)數(shù)之中,有一個(gè)二進(jìn)制位是符號(hào)位。這意味著,任何一個(gè)數(shù)都有一個(gè)對(duì)應(yīng)的負(fù)值,就連0也不例外。

JavaScript 內(nèi)部實(shí)際上存在2個(gè)0:一個(gè)是+0,一個(gè)是-0,區(qū)別就是64位浮點(diǎn)數(shù)表示法的符號(hào)位不同。它們是等價(jià)的。

-0 === +0 // true
0 === -0 // true
0 === +0 // true

幾乎所有場(chǎng)合,正零和負(fù)零都會(huì)被當(dāng)作正常的0

+0 // 0
-0 // 0
(-0).toString() // '0'
(+0).toString() // '0'

唯一有區(qū)別的場(chǎng)合是,+0-0當(dāng)作分母,返回的值是不相等的。

(1 / +0) === (1 / -0) // false

上面的代碼之所以出現(xiàn)這樣結(jié)果,是因?yàn)槌哉愕玫?code>+Infinity,除以負(fù)零得到-Infinity,這兩者是不相等的(關(guān)于Infinity詳見下文)。

NaN

(1)含義

NaN是 JavaScript 的特殊值,表示“非數(shù)字”(Not a Number),主要出現(xiàn)在將字符串解析成數(shù)字出錯(cuò)的場(chǎng)合。

5 - 'x' // NaN

上面代碼運(yùn)行時(shí),會(huì)自動(dòng)將字符串x轉(zhuǎn)為數(shù)值,但是由于x不是數(shù)值,所以最后得到結(jié)果為NaN,表示它是“非數(shù)字”(NaN)。

另外,一些數(shù)學(xué)函數(shù)的運(yùn)算結(jié)果會(huì)出現(xiàn)NaN

Math.acos(2) // NaN
Math.log(-1) // NaN
Math.sqrt(-1) // NaN

0除以0也會(huì)得到NaN。

0 / 0 // NaN

需要注意的是,NaN不是獨(dú)立的數(shù)據(jù)類型,而是一個(gè)特殊數(shù)值,它的數(shù)據(jù)類型依然屬于Number,使用typeof運(yùn)算符可以看得很清楚。

typeof NaN // 'number'

(2)運(yùn)算規(guī)則

NaN不等于任何值,包括它本身。

NaN === NaN // false

數(shù)組的indexOf方法內(nèi)部使用的是嚴(yán)格相等運(yùn)算符,所以該方法對(duì)NaN不成立。

[NaN].indexOf(NaN) // -1

NaN在布爾運(yùn)算時(shí)被當(dāng)作false。

Boolean(NaN) // false

NaN與任何數(shù)(包括它自己)的運(yùn)算,得到的都是NaN

NaN + 32 // NaN
NaN - 32 // NaN
NaN * 32 // NaN
NaN / 32 // NaN

Infinity

(1)含義

Infinity表示“無窮”,用來表示兩種場(chǎng)景。一種是一個(gè)正的數(shù)值太大,或一個(gè)負(fù)的數(shù)值太小,無法表示;另一種是非0數(shù)值除以0,得到Infinity

// 場(chǎng)景一
Math.pow(2, 1024)
// Infinity

// 場(chǎng)景二
0 / 0 // NaN
1 / 0 // Infinity

上面代碼中,第一個(gè)場(chǎng)景是一個(gè)表達(dá)式的計(jì)算結(jié)果太大,超出了能夠表示的范圍,因此返回Infinity。第二個(gè)場(chǎng)景是0除以0會(huì)得到NaN,而非0數(shù)值除以0,會(huì)返回Infinity。

Infinity有正負(fù)之分,Infinity表示正的無窮,-Infinity表示負(fù)的無窮。

Infinity === -Infinity // false

1 / -0 // -Infinity
-1 / -0 // Infinity

上面代碼中,非零正數(shù)除以-0,會(huì)得到-Infinity,負(fù)數(shù)除以-0,會(huì)得到Infinity

由于數(shù)值正向溢出(overflow)、負(fù)向溢出(underflow)和被0除,JavaScript 都不報(bào)錯(cuò),所以單純的數(shù)學(xué)運(yùn)算幾乎沒有可能拋出錯(cuò)誤。

Infinity大于一切數(shù)值(除了NaN),-Infinity小于一切數(shù)值(除了NaN)。

Infinity > 1000 // true
-Infinity < -1000 // true

InfinityNaN比較,總是返回false。

Infinity > NaN // false
-Infinity > NaN // false

Infinity < NaN // false
-Infinity < NaN // false

(2)運(yùn)算規(guī)則

Infinity的四則運(yùn)算,符合無窮的數(shù)學(xué)計(jì)算規(guī)則。

5 * Infinity // Infinity
5 - Infinity // -Infinity
Infinity / 5 // Infinity
5 / Infinity // 0

0乘以Infinity,返回NaN;0除以Infinity,返回0Infinity除以0,返回Infinity

0 * Infinity // NaN
0 / Infinity // 0
Infinity / 0 // Infinity

Infinity加上或乘以Infinity,返回的還是Infinity

Infinity + Infinity // Infinity
Infinity * Infinity // Infinity

Infinity減去或除以Infinity,得到NaN

Infinity - Infinity // NaN
Infinity / Infinity // NaN

Infinitynull計(jì)算時(shí),null會(huì)轉(zhuǎn)成0,等同于與0的計(jì)算。

null * Infinity // NaN
null / Infinity // 0
Infinity / null // Infinity

Infinityundefined計(jì)算,返回的都是NaN

undefined + Infinity // NaN
undefined - Infinity // NaN
undefined * Infinity // NaN
undefined / Infinity // NaN
Infinity / undefined // NaN

與數(shù)值相關(guān)的全局方法

parseInt()

(1)基本用法

parseInt方法用于將字符串轉(zhuǎn)為整數(shù)。

parseInt('123') // 123

如果字符串頭部有空格,空格會(huì)被自動(dòng)去除。

parseInt('   81') // 81

如果parseInt的參數(shù)不是字符串,則會(huì)先轉(zhuǎn)為字符串再轉(zhuǎn)換。

parseInt(1.23) // 1
// 等同于
parseInt('1.23') // 1

字符串轉(zhuǎn)為整數(shù)的時(shí)候,是一個(gè)個(gè)字符依次轉(zhuǎn)換,如果遇到不能轉(zhuǎn)為數(shù)字的字符,就不再進(jìn)行下去,返回已經(jīng)轉(zhuǎn)好的部分。

parseInt('8a') // 8
parseInt('12**') // 12
parseInt('12.34') // 12
parseInt('15e2') // 15
parseInt('15px') // 15

上面代碼中,parseInt的參數(shù)都是字符串,結(jié)果只返回字符串頭部可以轉(zhuǎn)為數(shù)字的部分。

如果字符串的第一個(gè)字符不能轉(zhuǎn)化為數(shù)字(后面跟著數(shù)字的正負(fù)號(hào)除外),返回NaN。

parseInt('abc') // NaN
parseInt('.3') // NaN
parseInt('') // NaN
parseInt('+') // NaN
parseInt('+1') // 1

所以,parseInt的返回值只有兩種可能,要么是一個(gè)十進(jìn)制整數(shù),要么是NaN。

如果字符串以0x0X開頭,parseInt會(huì)將其按照十六進(jìn)制數(shù)解析。

parseInt('0x10') // 16

如果字符串以0開頭,將其按照10進(jìn)制解析。

parseInt('011') // 11

對(duì)于那些會(huì)自動(dòng)轉(zhuǎn)為科學(xué)計(jì)數(shù)法的數(shù)字,parseInt會(huì)將科學(xué)計(jì)數(shù)法的表示方法視為字符串,因此導(dǎo)致一些奇怪的結(jié)果。

parseInt(1000000000000000000000.5) // 1
// 等同于
parseInt('1e+21') // 1

parseInt(0.0000008) // 8
// 等同于
parseInt('8e-7') // 8

(2)進(jìn)制轉(zhuǎn)換

parseInt方法還可以接受第二個(gè)參數(shù)(2到36之間),表示被解析的值的進(jìn)制,返回該值對(duì)應(yīng)的十進(jìn)制數(shù)。默認(rèn)情況下,parseInt的第二個(gè)參數(shù)為10,即默認(rèn)是十進(jìn)制轉(zhuǎn)十進(jìn)制。

parseInt('1000') // 1000
// 等同于
parseInt('1000', 10) // 1000

下面是轉(zhuǎn)換指定進(jìn)制的數(shù)的例子。

parseInt('1000', 2) // 8
parseInt('1000', 6) // 216
parseInt('1000', 8) // 512

上面代碼中,二進(jìn)制、六進(jìn)制、八進(jìn)制的1000,分別等于十進(jìn)制的8、216和512。這意味著,可以用parseInt方法進(jìn)行進(jìn)制的轉(zhuǎn)換。

如果第二個(gè)參數(shù)不是數(shù)值,會(huì)被自動(dòng)轉(zhuǎn)為一個(gè)整數(shù)。這個(gè)整數(shù)只有在2到36之間,才能得到有意義的結(jié)果,超出這個(gè)范圍,則返回NaN。如果第二個(gè)參數(shù)是0、undefinednull,則直接忽略。

parseInt('10', 37) // NaN
parseInt('10', 1) // NaN
parseInt('10', 0) // 10
parseInt('10', null) // 10
parseInt('10', undefined) // 10

如果字符串包含對(duì)于指定進(jìn)制無意義的字符,則從最高位開始,只返回可以轉(zhuǎn)換的數(shù)值。如果最高位無法轉(zhuǎn)換,則直接返回NaN。

parseInt('1546', 2) // 1
parseInt('546', 2) // NaN

上面代碼中,對(duì)于二進(jìn)制來說,1是有意義的字符,5、4、6都是無意義的字符,所以第一行返回1,第二行返回NaN

前面說過,如果parseInt的第一個(gè)參數(shù)不是字符串,會(huì)被先轉(zhuǎn)為字符串。這會(huì)導(dǎo)致一些令人意外的結(jié)果。

parseInt(0x11, 36) // 43
parseInt(0x11, 2) // 1

// 等同于
parseInt(String(0x11), 36)
parseInt(String(0x11), 2)

// 等同于
parseInt('17', 36)
parseInt('17', 2)

上面代碼中,十六進(jìn)制的0x11會(huì)被先轉(zhuǎn)為十進(jìn)制的17,再轉(zhuǎn)為字符串。然后,再用36進(jìn)制或二進(jìn)制解讀字符串17,最后返回結(jié)果431

這種處理方式,對(duì)于八進(jìn)制的前綴0,尤其需要注意。

parseInt(011, 2) // NaN

// 等同于
parseInt(String(011), 2)

// 等同于
parseInt(String(9), 2)

上面代碼中,第一行的011會(huì)被先轉(zhuǎn)為字符串9,因?yàn)?code>9不是二進(jìn)制的有效字符,所以返回NaN。如果直接計(jì)算parseInt('011', 2)011則是會(huì)被當(dāng)作二進(jìn)制處理,返回3。

JavaScript 不再允許將帶有前綴0的數(shù)字視為八進(jìn)制數(shù),而是要求忽略這個(gè)0。但是,為了保證兼容性,大部分瀏覽器并沒有部署這一條規(guī)定。

parseFloat()

parseFloat方法用于將一個(gè)字符串轉(zhuǎn)為浮點(diǎn)數(shù)。

parseFloat('3.14') // 3.14

如果字符串符合科學(xué)計(jì)數(shù)法,則會(huì)進(jìn)行相應(yīng)的轉(zhuǎn)換。

parseFloat('314e-2') // 3.14
parseFloat('0.0314E+2') // 3.14

如果字符串包含不能轉(zhuǎn)為浮點(diǎn)數(shù)的字符,則不再進(jìn)行往后轉(zhuǎn)換,返回已經(jīng)轉(zhuǎn)好的部分。

parseFloat('3.14more non-digit characters') // 3.14

parseFloat方法會(huì)自動(dòng)過濾字符串前導(dǎo)的空格。

parseFloat('\t\v\r12.34\n ') // 12.34

如果參數(shù)不是字符串,則會(huì)先轉(zhuǎn)為字符串再轉(zhuǎn)換。

parseFloat([1.23]) // 1.23
// 等同于
parseFloat(String([1.23])) // 1.23

如果字符串的第一個(gè)字符不能轉(zhuǎn)化為浮點(diǎn)數(shù),則返回NaN。

parseFloat([]) // NaN
parseFloat('FF2') // NaN
parseFloat('') // NaN

上面代碼中,尤其值得注意,parseFloat會(huì)將空字符串轉(zhuǎn)為NaN。

這些特點(diǎn)使得parseFloat的轉(zhuǎn)換結(jié)果不同于Number函數(shù)。

parseFloat(true)  // NaN
Number(true) // 1

parseFloat(null) // NaN
Number(null) // 0

parseFloat('') // NaN
Number('') // 0

parseFloat('123.45#') // 123.45
Number('123.45#') // NaN

isNaN()

isNaN方法可以用來判斷一個(gè)值是否為NaN。

isNaN(NaN) // true
isNaN(123) // false

但是,isNaN只對(duì)數(shù)值有效,如果傳入其他值,會(huì)被先轉(zhuǎn)成數(shù)值。比如,傳入字符串的時(shí)候,字符串會(huì)被先轉(zhuǎn)成NaN,所以最后返回true,這一點(diǎn)要特別引起注意。也就是說,isNaNtrue的值,有可能不是NaN,而是一個(gè)字符串。

isNaN('Hello') // true
// 相當(dāng)于
isNaN(Number('Hello')) // true

出于同樣的原因,對(duì)于對(duì)象和數(shù)組,isNaN也返回true。

isNaN({}) // true
// 等同于
isNaN(Number({})) // true

isNaN(['xzy']) // true
// 等同于
isNaN(Number(['xzy'])) // true

但是,對(duì)于空數(shù)組和只有一個(gè)數(shù)值成員的數(shù)組,isNaN返回false。

isNaN([]) // false
isNaN([123]) // false
isNaN(['123']) // false

上面代碼之所以返回false,原因是這些數(shù)組能被Number函數(shù)轉(zhuǎn)成數(shù)值,請(qǐng)參見《數(shù)據(jù)類型轉(zhuǎn)換》一章。

因此,使用isNaN之前,最好判斷一下數(shù)據(jù)類型。

function myIsNaN(value) {
  return typeof value === 'number' && isNaN(value);
}

判斷NaN更可靠的方法是,利用NaN為唯一不等于自身的值的這個(gè)特點(diǎn),進(jìn)行判斷。

function myIsNaN(value) {
  return value !== value;
}

isFinite()

isFinite方法返回一個(gè)布爾值,表示某個(gè)值是否為正常的數(shù)值。

isFinite(Infinity) // false
isFinite(-Infinity) // false
isFinite(NaN) // false
isFinite(undefined) // false
isFinite(null) // true
isFinite(-1) // true

除了Infinity、-Infinity、NaNundefined這幾個(gè)值會(huì)返回false,isFinite對(duì)于其他的數(shù)值都會(huì)返回true。

參考鏈接


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)