您現在的位置是:首頁 > 武術

詳述JavaScript的數字型別與數字系統

  • 由 龍騎士程式設計 發表于 武術
  • 2021-05-25
簡介三分之一變成了無限迴圈小數0

的傷勢12被除數和除數和為377 這個算式是多少

在現代 JavaScript 中,數字(number)有兩種型別:

JavaScript 中的常規數字以 64 位的格式 IEEE-754 儲存,也被稱為“雙精度浮點數”。這是我們大多數時候所使用的數字,我們將在本章中學習它們。

BigInt 數字,用於表示任意長度的整數。有時會需要它們,因為常規數字不能超過

253

或小於

-253

。由於僅在少數特殊領域才會用到 BigInt,因此我們在特殊的章節 BigInt 中對其進行了介紹。

所以,在這裡我們將討論常規數字型別。現在讓我們開始學習吧。

一、編寫數字的更多方法

想象一下,我們需要寫 10 億。顯然的方法是:

let billion = 1000000000;

但在現實生活中,我們通常避免寫一長串零,因為它很容易打錯。另外,我們很懶。我們通常會將 10 億寫成

“1bn”

,或將 73 億寫成

“7。3bn”

。對於大多數大的數字來說都是如此。

在 JavaScript 中,我們透過在數字後附加字母 “e”,並指定零的數量來縮短數字:

let billion = 1e9; // 10 億,字面意思:數字 1 後面跟 9 個 0alert( 7。3e9 ); // 73 億(7,300,000,000)

換句話說,

“e”

把數字乘以

1

後面跟著給定數量的 0 的數字。

1e3 = 1 * 10001。23e6 = 1。23 * 1000000

現在讓我們寫一些非常小的數字。例如,1 微秒(百萬分之一秒):

let ms = 0。000001;

就像以前一樣,可以使用

“e”

來完成。如果我們想避免顯式地寫零,我們可以這樣寫:

let ms = 1e-6; // 1 的左邊有 6 個 0

如果我們數一下

0。000001

中的 0 的個數,是 6 個。所以自然是

1e-6

換句話說,

e

後面的負數表示除以 1 後面跟著給定數量的 0 的數字:

// -3 除以 1 後面跟著 3 個 0 的數字1e-3 = 1 / 1000 (=0。001)// -6 除以 1 後面跟著 6 個 0 的數字1。23e-6 = 1。23 / 1000000 (=0。00000123)

二、十六進位制,二進位制和八進位制數字

十六進位制 數字在 JavaScript 中被廣泛用於表示顏色,編碼字元以及其他許多東西。所以自然地,有一種較短的寫方法:

0x

,然後是數字。

例如:

alert( 0xff ); // 255alert( 0xFF ); // 255(一樣,大小寫沒影響)

二進位制和八進位制數字系統很少使用,但也支援使用

0b

0o

字首:

let a = 0b11111111; // 二進位制形式的 255let b = 0o377; // 八進位制形式的 255alert( a == b ); // true,兩邊是相同的數字,都是 255

只有這三種進位制支援這種寫法。對於其他進位制,我們應該使用函式

parseInt

(我們將在本章後面看到)。

三、toString(base)

方法

num。toString(base)

返回在給定

base

進位制數字系統中

num

的字串表示形式。

舉個例子:

let num = 255;alert( num。toString(16) ); // ffalert( num。toString(2) ); // 11111111

base

的範圍可以從

2

36

。預設情況下是

10

常見的用例如下:

base=16

用於十六進位制顏色,字元編碼等,數字可以是

0。。9

A。。F

base=2

主要用於除錯按位操作,數字可以是

0

1

base=36

是最大進位制,數字可以是

0。。9

A。。Z

。所有拉丁字母都被用於了表示數字。對於

36

進位制來說,一個有趣且有用的例子是,當我們需要將一個較長的數字識別符號轉換成較短的時候,例如做一個短的 URL。可以簡單地使用基數為

36

的數字系統表示:alert

123456。

toString

36

);

// 2n9c

使用兩個點來呼叫一個方法

請注意

123456。。toString(36)

中的兩個點不是打錯了。如果我們想直接在一個數字上呼叫一個方法,比如上面例子中的

toString

,那麼我們需要在它後面放置兩個點

。。

如果我們放置一個點:

123456。toString(36)

,那麼就會出現一個 error,因為 JavaScript 語法隱含了第一個點之後的部分為小數部分。如果我們再放一個點,那麼 JavaScript 就知道小數部分為空,現在使用該方法。

也可以寫成

(123456)。toString(36)

四、舍入

舍入(rounding)是使用數字時最常用的操作之一。

這裡有幾個對數字進行舍入的內建函式:

Math。floor

向下舍入:

3。1

變成

3

-1。1

變成

-2

Math。ceil

向上舍入:

3。1

變成

4

-1。1

變成

-1

Math。round

向最近的整數舍入:

3。1

變成

3

3。6

變成

4

-1。1

變成

-1

Math。trunc

(IE 瀏覽器不支援這個方法)移除小數點後的所有內容而沒有舍入:

3。1

變成

3

-1。1

變成

-1

這個是總結它們之間差異的表格:

詳述JavaScript的數字型別與數字系統

這些函式涵蓋了處理數字小數部分的所有可能方法。但是,如果我們想將數字舍入到小數點後

n

位,該怎麼辦?

例如,我們有

1。2345

,並且想把它舍入到小數點後兩位,僅得到

1。23

有兩種方式可以實現這個需求:

乘除法例如,要將數字舍入到小數點後兩位,我們可以將數字乘以

100

(或更大的 10 的整數次冪),呼叫舍入函式,然後再將其除回。

let num = 1。23456;alert( Math。floor(num * 100) / 100 ); // 1。23456 -> 123。456 -> 123 -> 1。23

函式 toFixed(n) 將數字舍入到小數點後

n

位,並以字串形式返回結果。

let num = 12。34;alert( num。toFixed(1) ); // “12。3”

這會向上或向下舍入到最接近的值,類似於 Math。round:

let num = 12。36;alert( num。toFixed(1) ); // “12。4”

“請注意 toFixed 的結果是一個字串。如果小數部分比所需要的短,則在結尾新增零:

let num = 12。34;alert( num。toFixed(5) ); // ”12。34000“,在結尾添加了 0,以達到小數點後五位

我們可以使用一元加號或 Number() 呼叫,將其轉換為數字:+ num。toFixed(5)。

五、不精確的計算

在內部,數字是以 64 位格式 IEEE-754 表示的,所以正好有 64 位可以儲存一個數字:其中 52 位被用於儲存這些數字,其中 11 位用於儲存小數點的位置(對於整數,它們為零),而 1 位用於符號。

如果一個數字太大,則會溢位 64 位儲存,並可能會導致無窮大:

alert( 1e500 ); // Infinity

這可能不那麼明顯,但經常會發生的是,精度的損失。

考慮下這個(falsy!)測試:

alert( 0。1 + 0。2 == 0。3 ); // false

沒錯,如果我們檢查

0。1

0。2

的總和是否為

0。3

,我們會得到

false

奇了怪了!如果不是

0。3

,那能是啥?

alert( 0。1 + 0。2 ); // 0。30000000000000004

哎喲!這個錯誤比不正確的比較的後果更嚴重。想象一下,你建立了一個電子購物網站,如果訪問者將價格為

¥ 0。10

¥ 0。20

的商品放入了他的購物車。訂單總額將是

¥ 0。30000000000000004

。這會讓任何人感到驚訝。

但為什麼會這樣呢?

一個數字以其二進位制的形式儲存在記憶體中,一個 1 和 0 的序列。但是在十進位制數字系統中看起來很簡單的

0。1

0。2

這樣的小數,實際上在二進位制形式中是無限迴圈小數。

換句話說,什麼是

0。1

0。1

就是

1

除以

10

1/10

,即十分之一。在十進位制數字系統中,這樣的數字表示起來很容易。將其與三分之一進行比較:

1/3

。三分之一變成了無限迴圈小數

0。33333(3)

在十進位制數字系統中,可以保證以

10

的整數次冪作為除數能夠正常工作,但是以

3

作為除數則不能。也是同樣的原因,在二進位制數字系統中,可以保證以

2

的整數次冪作為除數時能夠正常工作,但

1/10

就變成了一個無限迴圈的二進位制小數。

使用二進位制數字系統無法

精確

儲存

0。1

0。2

,就像沒有辦法將三分之一儲存為十進位制小數一樣。

IEEE-754 數字格式透過將數字舍入到最接近的可能數字來解決此問題。這些舍入規則通常不允許我們看到“極小的精度損失”,但是它確實存在。

我們可以看到:

alert( 0。1。toFixed(20) ); // 0。10000000000000000555

當我們對兩個數字進行求和時,它們的“精度損失”會疊加起來。

這就是為什麼

0。1 + 0。2

不等於

0。3

不僅僅是 JavaScript

許多其他程式語言也存在同樣的問題。

PHP,Java,C,Perl,Ruby 給出的也是完全相同的結果,因為它們基於的是相同的數字格式。

我們能解決這個問題嗎?當然,最可靠的方法是藉助方法 toFixed(n) 對結果進行舍入:

let sum = 0。1 + 0。2;alert( sum。toFixed(2) ); // 0。30

請注意,

toFixed

總是返回一個字串。它確保小數點後有 2 位數字。如果我們有一個電子購物網站,並需要顯示

¥ 0。30

,這實際上很方便。對於其他情況,我們可以使用一元加號將其強制轉換為一個數字:

let sum = 0。1 + 0。2;alert( +sum。toFixed(2) ); // 0。3

我們可以將數字臨時乘以 100(或更大的數字),將其轉換為整數,進行數學運算,然後再除回。當我們使用整數進行數學運算時,誤差會有所減少,但仍然可以在除法中得到:

alert( (0。1 * 10 + 0。2 * 10) / 10 ); // 0。3alert( (0。28 * 100 + 0。14 * 100) / 100); // 0。4200000000000001

因此,乘/除法可以減少誤差,但不能完全消除誤差。

有時候我們可以嘗試完全避免小數。例如,我們正在建立一個電子購物網站,那麼我們可以用角而不是元來儲存價格。但是,如果我們要打 30% 的折扣呢?實際上,完全避免小數處理幾乎是不可能的。只需要在必要時剪掉其“尾巴”來對其進行舍入即可。

有趣的事兒

嘗試執行下面這段程式碼:

// Hello!我是一個會自我增加的數字!alert( 9999999999999999 ); // 顯示 10000000000000000

出現了同樣的問題:精度損失。有 64 位來表示該數字,其中 52 位可用於儲存數字,但這還不夠。所以最不重要的數字就消失了。

JavaScript 不會在此類事件中觸發 error。它會盡最大努力使數字符合所需的格式,但不幸的是,這種格式不夠大到滿足需求。

兩個零

數字內部表示的另一個有趣結果是存在兩個零:

0

-0

這是因為在儲存時,使用一位來儲存符號,因此對於包括零在內的任何數字,可以設定這一位或者不設定。

在大多數情況下,這種區別並不明顯,因為運算子將它們視為相同的值。

六、測試:isFinite 和 isNaN

還記得這兩個特殊的數值嗎?

Infinity

(和

-Infinity

)是一個特殊的數值,比任何數值都大(小)。

NaN

代表一個 error。

它們屬於

number

型別,但不是“普通”數字,因此,這裡有用於檢查它們的特殊函式:

isNaN(value)

將其引數轉換為數字,然後測試它是否為

alert( isNaN(NaN) ); // truealert( isNaN(”str“) ); // true

但是我們需要這個函式嗎?我們不能只使用 === NaN 比較嗎?不好意思,這不行。值 “NaN” 是獨一無二的,它不等於任何東西,包括它自身:

alert( NaN === NaN ); // false

isFinite(value)

將其引數轉換為數字,如果是常規數字,則返回

true

,而不是

NaN/Infinity/-Infinity

alert( isFinite(”15“) ); // truealert( isFinite(”str“) ); // false,因為是一個特殊的值:NaNalert( isFinite(Infinity) ); // false,因為是一個特殊的值:Infinity

有時

isFinite

被用於驗證字串值是否為常規數字:

let num = +prompt(”Enter a number“, ‘’);// 結果會是 true,除非你輸入的是 Infinity、-Infinity 或不是數字alert( isFinite(num) );

請注意,在所有數字函式中,包括

isFinite

,空字串或僅有空格的字串均被視為

0

Object。is

進行比較

有一個特殊的內建方法

Object。is

,它類似於

===

一樣對值進行比較,但它對於兩種邊緣情況更可靠:

它適用於

NaN

Object。is(NaN,NaN)=== true

,這是件好事。

0

-0

是不同的:

Object。is(0,-0)=== false

,從技術上講這是對的,因為在內部,數字的符號位可能會不同,即使其他所有位均為零。

在所有其他情況下,

Object。is(a,b)

a === b

相同。

這種比較方式經常被用在 JavaScript 規範中。當內部演算法需要比較兩個值是否完全相同時,它使用

Object。is

(內部稱為 SameValue)。

七、parseInt 和 parseFloat

使用加號

+

Number()

的數字轉換是嚴格的。如果一個值不完全是一個數字,就會失敗:

alert( +”100px“ ); // NaN

唯一的例外是字串開頭或結尾的空格,因為它們會被忽略。

但在現實生活中,我們經常會有帶有單位的值,例如 CSS 中的

”100px“

”12pt“

。並且,在很多國家,貨幣符號是緊隨金額之後的,所以我們有

”19€“

,並希望從中提取出一個數值。

這就是

parseInt

parseFloat

的作用。

它們可以從字串中“讀取”數字,直到無法讀取為止。如果發生 error,則返回收集到的數字。函式

parseInt

返回一個整數,而

parseFloat

返回一個浮點數:

alert( parseInt(‘100px’) ); // 100alert( parseFloat(‘12。5em’) ); // 12。5alert( parseInt(‘12。3’) ); // 12,只有整數部分被返回了alert( parseFloat(‘12。3。4’) ); // 12。3,在第二個點出停止了讀取

某些情況下,

parseInt/parseFloat

會返回

NaN

。當沒有數字可讀時會發生這種情況:

alert( parseInt(‘a123’) ); // NaN,第一個符號停止了讀取

parseInt(str, radix)` 的第二個引數

parseInt()

函式具有可選的第二個引數。它指定了數字系統的基數,因此

parseInt

還可以解析十六進位制數字、二進位制數字等的字串:

alert( parseInt(‘0xff’, 16) ); // 255alert( parseInt(‘ff’, 16) ); // 255,沒有 0x 仍然有效alert( parseInt(‘2n9c’, 36) ); // 123456

八、其他數學函式

JavaScript 有一個內建的 Math 物件,它包含了一個小型的數學函式和常量庫。

幾個例子:

Math。random()

返回一個從 0 到 1 的隨機數(不包括 1)

alert( Math。random() ); // 0。1234567894322alert( Math。random() ); // 0。5435252343232alert( Math。random() ); // 。。。 (任何隨機數)

Math。max(a, b, c。。。)

/

Math。min(a, b, c。。。)

從任意數量的引數中返回最大/最小值。

alert( Math。max(3, 5, -10, 0, 1) ); // 5alert( Math。min(1, 2) ); // 1

Math。pow(n, power)

返回

n

的給定(power)次冪

alert( Math。pow(2, 10) ); // 2 的 10 次冪 = 1024

Math

物件中還有更多函式和常量,包括三角函式,你可以在 Math 物件文件 中找到這些內容。

九、總結

要寫有很多零的數字:

”e“

和 0 的數量附加到數字後。就像:

123e6

123

後面接 6 個 0 相同。

”e“

後面的負數將使數字除以 1 後面接著給定數量的零的數字。例如

123e-6

表示

0。000123

123

的百萬分之一)。

對於不同的數字系統:

可以直接在十六進位制(

0x

),八進位制(

0o

)和二進位制(

0b

)系統中寫入數字。

parseInt(str,base)

將字串

str

解析為在給定的

base

數字系統中的整數,

2 ≤ base ≤ 36

num。toString(base)

將數字轉換為在給定的

base

數字系統中的字串。

要將

12pt

100px

之類的值轉換為數字:

使用

parseInt/parseFloat

進行“軟”轉換,它從字串中讀取數字,然後返回在發生 error 前可以讀取到的值。

小數:

使用

Math。floor

Math。ceil

Math。trunc

Math。round

num。toFixed(precision)

進行舍入。

請確保記住使用小數時會損失精度。

更多數學函式:

需要時請檢視 Math 物件。這個庫很小,但是可以滿足基本的需求。

Top