JavaScript 在數值的小數點與整數的處理上有著很多的細節,在操作過後才了解到不單純只是整數與小數等一些運算,其中也包涵著浮點數、進制方法處理的差別,先以了解的方式針了理解的地方記錄。
數值概念
整數和浮點數
JavaScript 整數與相關數值,預設 64 位浮點數形式儲存,1 與 1.0 是相同。
1 | 1 === 1.0 // true |
底層根本沒有整數,所有數字都是小數。
1 2 3 4 | 0.1 + 0.2; // 0.30000000000000004 0.1 + 0.2 === 0.3; // false 0.3 / 0.1; // 2.9999999999999996 (0.3 - 0.2) === (0.2 - 0.1); // false |
數值精度
64 二進制位最左邊開始。
- 第 1 位:符號位,0 表示正數,1 表示負數
- 第 2 位到第 12 位(共 11 位):指數部分
- 第 13 位到第 64 位(共 52 位):小數部分(即有效數字)
符號位決定了一個數的正負,指數部分決定了數值的大小,小數部分決定了數值的精度。
指數部分的值在 0 到 2047 之間(不含兩個端點),效數字的第一位默認總是 1,不保存在 64 位浮點數之中。
64 位浮點數之中,最長可能為 52 位,效數字最長為 53 個二進制位。
大於 2 的 53 次方以後,整數運算的結果開始出現錯誤。所以,大於 2 的 53 次方的數值,都無法保持精度。對 15 位的十進制數都可以精確處理。
1 2 3 4 5 6 | Math.pow(2, 53) // 9007199254740992 // 多出的三個有效數字,將無法保存 9007199254740992111 // 9007199254740992000 |
大於 2 的 53 次方以後,多出來的有效數字(最後三位的 111)都會無法保存,變成 0。
數值的表示法
數值有多種表示方法,35(十進制)和 0xFF(十六進制)
數值也可以採用科學計數法表示,下面是幾個科學計數法的例子。
1 2 3 4 | 123e3 // 123000 123e-3 // 0.123 -3.1E+12 .1e-23 |
科學計數法允許字母 e 或 E 的後面,跟著一個整數,表示這個數值的指數部分,兩種情況自動將數值轉為科學計數法表示,其他情況都採用字面形式直接表示。
小數點前的數字多於 21 位。
1 2 3 4 5 | 1234567890123456789012 // 1.2345678901234568e+21 123456789012345678901 // 123456789012345680000 |
小數點後的零多於 5 個。
1 2 3 4 5 6 | // 小數點後緊跟5個以上的零, // 就自動轉為科學計數法 0.0000003 // 3e-7 // 否則,就保持原來的字面形式 0.000003 // 0.000003 |
數值的進制
使用字面量 (literal) 直接表示一個數值時,對整數提供四種進制的表示方法:
- 十進制:沒有前導0的數值。
- 八進制:有前綴 0o 或 0O 的數值,或者有前導 0、且只用到 0 – 7 的八個阿拉伯數字的數值。
- 十六進制:有前綴 0x 或 0X 的數值。
- 二進制:有前綴 0b 或 0B 的數值。
預設八進制、十六進制、二進制轉為十進制。
1 2 3 | 0xff // 255 0o377 // 255 0b11 // 3 |
八進制、十六進制、二進制的數值裡面,出現不屬於該進制的數字,就會報錯。十六進制出現了字母 z、八進制出現數字 8、二進制出現數字 2,因此報錯。
1 2 3 | 0xzz // 報錯 0o88 // 報錯 0b22 // 報錯 |
有前導 0 的數值會被視為八進制,但是如果前導 0 後面有數字 8 和 9,則該數值被視為十進制。
1 2 | 0888 // 888 0777 // 511 |
前導 0 表示八進制,處理時很容易造成混亂。ES5 的嚴格模式和 ES6,已經廢除了這種表示法,但是瀏覽器為了兼容以前的代碼,目前還繼續支持這種表示法。
特殊數值
正零和負零
64 位浮點數之中,有一個二進制位是符號位。一個數都有一個對應的負值,就連 0 也分 +0 與 -0,區別就是 64 位浮點數表示法的符號位不同但是等價。
1 2 3 | -0 === +0 // true 0 === -0 // true 0 === +0 // true |
+0 或 -0 當作分母,返回的值是不相等的。
1 | (1 / +0) === (1 / -0) // false |
除以正零得到 +Infinity,除以負零得到 -Infinity,這兩者是不相等的。
NaN
NaN 非數字 (Not a Number) 將字符串解析成數字出錯時表示。
1 | 5 - 'x' // NaN |
透過數值處理的方法也是,當不符合對應的設定,也會出現 NaN。
1 2 3 | Math.acos(2) // NaN Math.log(-1) // NaN Math.sqrt(-1) // NaN |
0 除以 0 也會得到 NaN。
1 | 0 / 0 // NaN |
NaN 是一個特殊數值屬於 Number。
1 | typeof NaN // 'number' |
NaN 不等於任何值包括本身。
1 | NaN === NaN // false |
陣列中的 indexOf 方法是嚴格相等運算符,該方法對 NaNa 不成立。
1 | [NaN].indexOf(NaN) // -1 |
NaN 在布林運算時被當作 false
1 | Boolean(NaN) // false |
NaN 與任何數(包括它自己)的運算,得到的都是 NaN。
1 2 3 4 | NaN + 32 // NaN NaN - 32 // NaN NaN * 32 // NaN NaN / 32 // NaN |
轉換方法
parseInt() 字串轉為整數與進制轉換
基本用法
將字串轉為整數
1 | parseInt('123') // 123 |
字串頭部有空格,空格會被自動去除
1 | parseInt(' 81') // 81 |
parseInt 的參數不是字串,則會先轉為字串再轉換。
1 2 3 | parseInt(1.23) // 1 // 等同於 parseInt('1.23') // 1 |
字串轉整數時逐一將字符依次轉換,遇到不能轉為數字的字符就不再進行下去,將轉好的部分傳回出。
1 2 3 4 5 | parseInt('8a') // 8 parseInt('12**') // 12 parseInt('12.34') // 12 parseInt('15e2') // 15 parseInt('15px') // 15 |
字串以 0 開頭,將其按照 10 進制解析。
1 | parseInt('011') // 11 |
會自動轉為科學計數法的數字,表示方法視為字串,因此導致一些奇怪的結果。
1 2 3 4 5 6 7 | parseInt(1000000000000000000000.5) // 1 // 等同於 parseInt('1e+21') // 1 parseInt(0.0000008) // 8 // 等同於 parseInt('8e-7') // 8 |
進制轉換
parseInt 第二個參數( 2 到 36 之間)解析的值的進制,預設是十進制處理,透過第二參數指定特定的進制數,二進制、六進制、八進制的 1000 可以用 parseInt 方法進行進制的轉換。。
1 2 3 | parseInt('1000', 2) // 8 parseInt('1000', 6) // 216 parseInt('1000', 8) // 512 |
二個參數不是數值,會被自動轉為一個整數,設定參數只有在 2 到 36 之間,設定超過會回應出 NaN。
1 2 | parseInt('10', 37) // NaN parseInt('10', 1) // NaN |
二個參數數值設定 0 會直接以十進位運算。
1 | parseInt('10', 0) // 10 |
undefined 和 null 直接忽略。
1 2 | parseInt('10', null) // 10 parseInt('10', undefined) // 10 |
二進制只針對第一行個數值以 0 與 1 處理,其他後面的位數都會視為沒有意義,如果直接操過 1 的數字回應 NaN。
1 2 3 | parseInt('1546', 2) // 1 parseInt('0546', 2) // 0 parseInt('546', 2) // NaN |
isNaN() 判斷非數字
用來判斷一個值是否為非數字 (Not a Number),由下例得知非數值會得到 true,而數值得到的是 false。
1 2 | isNaN(NaN) // true isNaN(123) // false |
isNaN 只對數值,傳入其他值會先轉成數值。 isNaN 為 true 的值可能不是 NaN,而是一個字串。
1 2 3 | isNaN('Hello') // true // 相當於 isNaN(Number('Hello')) // true |
物件和陣列 isNaN 也傳回 true。
1 2 3 4 5 6 7 | isNaN({}) // true // 等同於 isNaN(Number({})) // true isNaN(['xzy']) // true // 等同於 isNaN(Number(['xzy'])) // true |
但在陣列要特別注意,其中裡面還有的值,值是字串數字或是純數值,就會回傳出來的是 false,陣列能被 Number 函式轉成數值再向 isNaN 處理。
1 2 3 | isNaN([]) // false isNaN([123]) // false isNaN(['123']) // false |
isFinite() 值是否為正常的數值
isFinite 方法傳回一個布林值,表示某個值是否為正常的數值,以數值來說一定會得到的是 true,特別注意 null 所得到的也是 true。
1 2 3 4 5 6 | isFinite(Infinity) // false isFinite(-Infinity) // false isFinite(NaN) // false isFinite(undefined) // false isFinite(null) // true isFinite(-1) // true |
判斷正數還是負數
以 0 數值以 if else 做為判斷
1 2 3 4 5 6 7 8 9 | var a = 1; if (a > 0) { console.log('正數'); } else if (a < 0) { console.log('負數'); } else { console.log('都不是'); } // 正數 |
Math.sign(x) 函數返回一個數字的符號,指示數字是正數,負數還是零。
sign 是 Math 的一個靜態方法,使用 Math.sign()。
5 種返回值:
- 1 (正數)
- -1 (負數)
- 0 (正零)
- -0 (負零)
- NaN (非數值)
1 2 3 4 5 6 | const positive = 5; const negative = -5; const zero = 0; Math.sign(positive); // 1 Math.sign(negative); // -1 Math.sign(zero); // 0 |