JS 的數字具有以下特徵:
0
,則可被省略,如:.42
和 0.42
一樣是個合法的數字0
,也同樣可被省略。比如 42.
和 42.0000
都是合法數字0
toExponential
方法的結果一樣,唯一區別是後者會將結果轉為字串var a = 0.42;
var b = .42;
var c = 42.;
var d = 42.00000;
var e = 42.344500
var f = 5000000000000000000000;
var g = f.toExponential();
console.log(a); // 0.42
console.log(b); // 0.42
console.log(c); // 42
console.log(d); // 42
console.log(e); // 42.3445
console.log(f); // 5e+21
console.log(g); // "5e+21"
toFixed
toFixed
能夠指定一個數字被表示時顯示多少位小數,並以字串來表示。如果指定的位數多於實際持有的小數位數,則會在右側補 0
。
let a = 42.59;
console.log(a.toFixed(0)); // "43"
console.log(a.toFixed(1)); // "42.6"
console.log(a.toFixed(2)); // "42.59"
console.log(a.toFixed(3)); // "42.590"
console.log(a.toFixed(4)); // "42.5900"
toPrecision
跟 toFixed
類似,只是它是指定共有多少有效位數來表示這個數字,同樣是回傳一個字串。
let a = 42.59;
console.log(a.toPrecision(1)); // "4e+1"
console.log(a.toPrecision(2)); // "43"
console.log(a.toPrecision(3)); // "42.6"
console.log(a.toPrecision(4)); // "42.59"
console.log(a.toPrecision(5)); // "42.590"
console.log(a.toPrecision(6)); // "42.5900"
.
操作符由於 .
符號同時也是個合法的數字字符,所以當它遇到數字時,首先位被視為 number
的一部分,而不是一個屬性訪問操作符。
不合法的語法:
42.toFixed(3);
// SyntaxError: Invalid or unexpected token
合法的語法:
(42).toFixed(3); // "42.000"
0.42.toFixed(3); // "0.420"
42..toFixed(3); // "42.000"
number
字面量除了十進制,也可以用其他進制表達:
0xf3; // 十六進制的 243
0Xf3; // 十六進制的 243
ES6+:
0o363; //八進制的 243
0O363; //八進制的 243
0b11110011; // 二進制的 243
0B11110011; // 二進制的 243
二進制浮點數有個最著名的副作用,在所有使用 IEEE 754 的語言中都成立:
0.1 + 0.2 === 0.3; // false
以上算式不成立的原因簡單來說,二進制無法精確地表示 0.1
和 0.2
,所以當它們相加時,結果不是精確的 0.3
,而是 0.30000000000000004
。
大部分時候,我們對數字的操作只限於整數,而且僅在百萬到億萬大小的數字,這些操作都是安全的。只是在ㄧ些特殊情況下,像是對 0.1 + 0.2
與 0.3
比較時出現的誤差,要怎麼知道和預防呢?
可以接受的常見做法是使用一個很小的值作為錯誤的「容差(rounding error)」,通常被稱為「機械極小值(machine epsilon)」。
JS 在 ES6 中定義了 Number.EPSILON
作為容差值,其值是 2^-52
(也就是 2.220446049250313e-16
)。在 ES6 以前的填補會是:
if (!Number.EPSILON) {
Number.EPSILON = Math.pow(2,-52);
}
使用 Number.EPSILON
比較兩個數字的相等性時,可以帶入這個容差:
function numbersCloseEnoughToEqual(n1, n2) {
return Math.abs(n1 - n2) < Number.EPSILON;
}
console.log(numbersCloseEnoughToEqual((0.1 + 0.2), 0.3)); // true
console.log(numbersCloseEnoughToEqual(0.0000001, 0.0000002)); // false
JS 中最大的數是 Number.MAX_VALUE
,表示能夠被表示的最大浮點數,大約是 1.798e+308
;最小的數則是 Number.MIN_VALUE
,約為 5e-324
。
一個能夠安全表示,也就是能正確無誤表示出來的整數比 Number.MAX_VALUE
要小,只有 2^53 - 1
,也就是 9007199254740991
,但對於絕大多數情況下需要的計算已經足夠大。
ES6 以後這個數被自動定義於 Number.MAX_SAFE_INTEGER
,同時也出現了 Number.MIN_SAFE_INTEGER
,它的值是 -9007199254740991
。
JS 程式面臨如此巨大數字時,無法使用 number
型別準確表達,因此在 JS 中必須使用 string
來表現、儲存和傳遞。
BigInt
在操作大於或小於最大/最小安全整數時,除了引入外部工具,也可以使用 BigInt
這個 ES11 新增的內建物件,表示大於 2^53
的整數。
BigInt
透過調用 BigInt()
,或在數字後面加上 n
來創建,另外 BigInt
的檢測型別是 bigint
:
console.log(BigInt(Number.MAX_SAFE_INTEGER)); // 9007199254740991n
console.log(BigInt("9007199254740991")); // 9007199254740991n
console.log(BigInt("0x1fffffffffffff")); // 9007199254740991n
// 9007199254740991n 是一個合法的數字
console.log(typeof 9007199254740991n); // bigint
console.log(typeof BigInt(1)); // bigint
// 但使用 Object 包裹時,會被視為普通的物件
console.log(typeof Object(1n)); // object
BigInt
無法用於內建的 Math
方法中:
console.log(Math.abs(BigInt(1n) - BigInt(2n)));
/// TypeError: Cannot convert a BigInt value to a number
也不能與 Number
混合計算:
console.log(BigInt(Number.MAX_SAFE_INTEGER) + BigInt(Number.MAX_SAFE_INTEGER));
// 18014398509481982n
console.log(Number(Number.MAX_SAFE_INTEGER) + Number(Number.MAX_SAFE_INTEGER));
// 18014398509481982
console.log(Object(9007199254740991n) + Object(9007199254740991n));
// 18014398509481982n
console.log(BigInt(Number.MAX_SAFE_INTEGER) + Number(Number.MAX_SAFE_INTEGER));
// TypeError: Cannot mix BigInt and other types, use explicit conversions
由於 BigInt
字面意義是「大數字的整數」,所以不接受包含小數的數字:
console.log(Number(0.42));
// 0.42
console.log(BigInt(0.42n));
// SyntaxError: Invalid or unexpected token
運算時帶小數的部分也會被捨去:
console.log(4n / 2n); // 2n
console.log(5n / 2n); // 2n
一個 BitInt
無法嚴格相等等值的 Number
,但它們彼此是寬鬆相等的:
console.log(0n === 0); // false
console.log(0n == 0); // true
BitInt
與 Number
能夠進行大於/小於比較:
console.log(1n < 2); // true
console.log(2n > 1); // true
console.log(2 > 2); // false
console.log(2n > 2); // false
console.log(2n >= 2); // true
BitInt
能夠執行 sort
的排序,且按照預期排列:
const mixed = [4n, 6, -12n, 10, 4, 0, 0n];
mixed.sort();
console.log(mixed);
// [ -12n, 0, 0n, 10, 4n, 4, 6]
由 Object
包裹的 BitInt
在執行相等比較時會被視為物件,但在大小於比較中被視為數字:
console.log(0n === Object(0n)); // false
console.log(Object(0n) === Object(0n)) // false
console.log(Object(0n) == Object(0n)) // false
const o = Object(0n);
console.log(o === o) // true
console.log(Object(0n) > Object(0n)) // false
console.log(Object(0n) >= Object(0n)) // true
Number.isInteger
(ES6+)可以測試一個數是否為整數:
Number.isInteger(42); // true
Number.isInteger(42.000); // true
Number.isInteger(42.3); // false
ES6 以前的填補:
if (!Number.isInteger) {
Number.isInteger = function (num) {
return typeof num == "number" && num % 1 == 0;
};
}
Number.isSafeInteger
(ES6+)測試一個數是否為安全整數:
Number.isSafeInteger(Number.MAX_SAFE_INTEGER); // true
Number.isSafeInteger(Math.pow(2, 53)); // false
Number.isSafeInteger(Math.pow(2, 53) - 1); // true
ES6 以前的填補:
if (!Number.isSafeInteger) {
Number.isSafeInteger = function (num) {
return Number.isInteger(num) &&
Math.abs(num) <= Number.MAX_SAFE_INTEGER;
};
}
雖然最大安全整數為九萬億(53 bit)左右,但有些數字操作僅是為 32 位數字定義的,實際安全範圍要小的多,僅在 Math.pow(-2,31)
(-2147483648
)到 Math.pow(2,31)-1
(2147483647
) 之間。
要強制操作中的變數容納的數字為 32 位值,可以使用 |
符號,比方說 a | 0
,因為 |
符號僅對 32 位值有效,其他數字將被丟棄。
NaN
和Infinity
不屬於「32 位安全(32-bit safe)」的數字,這兩個值被傳入位元運算子(bitwise operator)時,將會通過一個內部的ToInt32
操作,並為了位元運算變成+0
。
幫忙 updata 一下
其實現在已經有原生的 BigInt 支援大數運算了,可以參考 BigInt - JavaScript | MDN 介紹
如果瀏覽器不支援的話,還有第三方套件支援,可參考 MikeMcl/bignumber.js
@sixwings
感謝幫捉 BUG!火速來改
事實證明寫技術文還是有用的
馬上又現學一個新知XD
BigInt 的部分剛緊急研究了一下,已更新!
不客氣,剛好看到就回一下
不確定你還要更新多久?
但感覺蠻用心在寫系列的,加油唷 XD
@sixwings 最初開賽是打算把YDNJS到第五冊的筆記寫完
到第30篇的時候我以為60篇內可以寫完
現在的話大概全部會是70篇左右...吧?