“睡服”面试官系列第九篇之数值的扩展(建议收藏学习)
目錄
1. 二進制和八進制表示法
2. Number.isFinite(), Number.isNaN()
3. Number.parseInt(), Number.parseFloat()
4. Number.isInteger()
5. Number.EPSILON
6. 安全整數和 Number.isSafeInteger()
7. Math 對象的擴展
7.1Math.trunc()
7.2Math.sign()
7.3Math.cbrt()
7.4Math.clz32()
7.5Math.imul()
7.7Math.hypot()
8對數方法
8.1Math.expm1()
8.2Math.log1p()
8.3Math.log10()
8.4Math.log2()
9雙曲函數方法
10. Math.signbit()
11. 指數運算符
12Integer 數據類型
12.1簡介
12.2運算
總結
“睡服“面試官系列之各系列目錄匯總(建議學習收藏)
1. 二進制和八進制表示法
ES6 提供了二進制和八進制數值的新的寫法,分別用前綴 0b (或 0B )和 0o (或 0O )表示。
0b111110111 === 503 // true 0o767 === 503 // true // 非嚴格模式 (function(){ console.log(0o11 === 011); })() // true // 嚴格模式 (function(){ 'use strict'; console.log(0o11 === 011); })() // Uncaught SyntaxError: Octal literals are not allowed in strict mode.如果要將 0b 和 0o 前綴的字符串數值轉為十進制,要使用 Number 方法。
Number('0b111') // 7 Number('0o10') // 82. Number.isFinite(), Number.isNaN()
ES6 在 Number 對象上,新提供了 Number.isFinite() 和 Number.isNaN() 兩個方法。
Number.isFinite() 用來檢查一個數值是否為有限的(finite)
ES5 可以通過下面的代碼,部署 Number.isFinite 方法。
(function (global) { var global_isFinite = global.isFinite; Object.defineProperty(Number, 'isFinite', { value: function isFinite(value) { return typeof value === 'number' && global_isFinite(value); }, configurable: true, enumerable: false, writable: true }); })(this);Number.isNaN() 用來檢查一個值是否為 NaN 。
Number.isNaN(NaN) // true Number.isNaN(15) // false Number.isNaN('15') // false Number.isNaN(true) // false Number.isNaN(9/NaN) // true Number.isNaN('true'/0) // true Number.isNaN('true'/'true') // trueES5 通過下面的代碼,部署 Number.isNaN() 。
(function (global) { var global_isNaN = global.isNaN; Object.defineProperty(Number, 'isNaN', { value: function isNaN(value) { return typeof value === 'number' && global_isNaN(value); }, configurable: true, enumerable: false, writable: true }); })(this);它們與傳統的全局方法 isFinite() 和 isNaN() 的區別在于,傳統方法先調用 Number() 將非數值的值轉為數值,再進行判斷,而這兩個新方法只對數值有
效, Number.isFinite() 對于非數值一律返回 false , Number.isNaN() 只有對于 NaN 才返回 true ,非 NaN 一律返回 false 。
3. Number.parseInt(), Number.parseFloat()
ES6 將全局方法 parseInt() 和 parseFloat() ,移植到 Number 對象上面,行為完全保持不變
// ES5的寫法 parseInt('12.34') // 12 parseFloat('123.45#') // 123.45 // ES6的寫法 Number.parseInt('12.34') // 12 Number.parseFloat('123.45#') // 123.45這樣做的目的,是逐步減少全局性方法,使得語言逐步模塊化。
Number.parseInt === parseInt // true Number.parseFloat === parseFloat // true4. Number.isInteger()
Number.isInteger() 用來判斷一個值是否為整數。需要注意的是,在 JavaScript 內部,整數和浮點數是同樣的儲存方法,所以 3 和 3.0 被視為同一個
值。
ES5 可以通過下面的代碼,部署 Number.isInteger()
(function (global) { var floor = Math.floor, isFinite = global.isFinite; Object.defineProperty(Number, 'isInteger', { value: function isInteger(value) { return typeof value === 'number' && isFinite(value) && floor(value) === value; }, configurable: true, enumerable: false, writable: true }); })(this)5. Number.EPSILON
ES6 在 Number 對象上面,新增一個極小的常量 Number.EPSILON 。根據規格,它表示 1 與大于 1 的最小浮點數之間的差。
對于 64 位浮點數來說,大于 1 的最小浮點數相當于二進制的 1.00..001 ,小數點后面有連續 51 個零。這個值減去 1 之后,就等于 2 的-52 次方
Number.EPSILON 實際上是 JavaScript 能夠表示的最小精度。誤差如果小于這個值,就可以認為已經沒有意義了,即不存在誤差了。
引入一個這么小的量的目的,在于為浮點數計算,設置一個誤差范圍。我們知道浮點數計算是不精確的。
上面代碼解釋了,為什么比較 0.1 + 0.2 與 0.3 得到的結果是 false 。
0.1 + 0.2 === 0.3 // falseNumber.EPSILON 可以用來設置“能夠接受的誤差范圍”。比如,誤差范圍設為 2 的-50 次方(即 Number.EPSILON * Math.pow(2, 2) ),即如果兩個浮點
數的差小于這個值,我們就認為這兩個浮點數相等。
因此, Number.EPSILON 的實質是一個可以接受的最小誤差范圍。
function withinErrorMargin (left, right) { return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2); } 0.1 + 0.2 === 0.3 // false withinErrorMargin(0.1 + 0.2, 0.3) // true 1.1 + 1.3 === 2.4 // false withinErrorMargin(1.1 + 1.3, 2.4) // true上面的代碼為浮點數運算,部署了一個誤差檢查函數。
6. 安全整數和 Number.isSafeInteger()
JavaScript 能夠準確表示的整數范圍在 -2^53 到 2^53 之間(不含兩個端點),超過這個范圍,無法精確表示這個值。
Math.pow(2, 53) // 9007199254740992 9007199254740992 // 9007199254740992 9007199254740993 // 9007199254740992 Math.pow(2, 53) === Math.pow(2, 53) + 1 // true上面代碼中,超出 2 的 53 次方之后,一個數就不精確了。
ES6 引入了 Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER 這兩個常量,用來表示這個范圍的上下限
上面代碼中,可以看到 JavaScript 能夠精確表示的極限。
Number.isSafeInteger() 則是用來判斷一個整數是否落在這個范圍之內
這個函數的實現很簡單,就是跟安全整數的兩個邊界值比較一下
?
實際使用這個函數時,需要注意。驗證運算結果是否落在安全整數的范圍內,不要只驗證運算結果,而要同時驗證參與運算的每個值
Number.isSafeInteger(9007199254740993) // false Number.isSafeInteger(990) // true Number.isSafeInteger(9007199254740993 - 990) // true 9007199254740993 - 990 // 返回結果 9007199254740002 // 正確答案應該是 9007199254740003上面代碼中, 9007199254740993 不是一個安全整數,但是 Number.isSafeInteger 會返回結果,顯示計算結果是安全的。這是因為,這個數超出了精度范
圍,導致在計算機內部,以 9007199254740992 的形式儲存。
所以,如果只驗證運算結果是否為安全整數,很可能得到錯誤結果。下面的函數可以同時驗證兩個運算數和運算結果。
function trusty (left, right, result) { if ( Number.isSafeInteger(left) && Number.isSafeInteger(right) && Number.isSafeInteger(result) ) { return result; } throw new RangeError('Operation cannot be trusted!'); } trusty(9007199254740993, 990, 9007199254740993 - 990) // RangeError: Operation cannot be trusted! trusty(1, 2, 3) // 37. Math 對象的擴展
ES6 在 Math 對象上新增了 17 個與數學相關的方法。所有這些方法都是靜態方法,只能在 Math 對象上調用
7.1Math.trunc()
Math.trunc 方法用于去除一個數的小數部分,返回整數部分
Math.trunc(4.1) // 4 Math.trunc(4.9) // 4 Math.trunc(-4.1) // -4 Math.trunc(-4.9) // -4 Math.trunc(-0.1234) // -0對于非數值, Math.trunc 內部使用 Number 方法將其先轉為數值。
Math.trunc('123.456') // 123 Math.trunc(true) //1 Math.trunc(false) // 0 Math.trunc(null) // 0對于空值和無法截取整數的值,返回 NaN
Math.trunc(NaN); // NaN Math.trunc('foo'); // NaN Math.trunc(); // NaN Math.trunc(undefined) // NaN對于沒有部署這個方法的環境,可以用下面的代碼模擬
Math.trunc = Math.trunc || function(x) { return x < 0 ? Math.ceil(x) : Math.floor(x); };7.2Math.sign()
Math.sign 方法用來判斷一個數到底是正數、負數、還是零。對于非數值,會先將其轉換為數值。
它會返回五種值
參數為正數,返回 +1 ;
參數為負數,返回 -1 ;
參數為 0,返回 0 ;
參數為-0,返回 -0 ;
其他值,返回 NaN 。
如果參數是非數值,會自動轉為數值。對于那些無法轉為數值的值,會返回 NaN 。
Math.sign('') // 0 Math.sign(true) // +1 Math.sign(false) // 0 Math.sign(null) // 0 Math.sign('9') // +1 Math.sign('foo') // NaN Math.sign() // NaN Math.sign(undefined) // NaN對于沒有部署這個方法的環境,可以用下面的代碼模擬。
Math.sign = Math.sign || function(x) { x = +x; // convert to a number if (x === 0 || isNaN(x)) { return x; } return x > 0 ? 1 : -1; };7.3Math.cbrt()
Math.cbrt 方法用于計算一個數的立方根
Math.cbrt(-1) // -1 Math.cbrt(0) // 0 Math.cbrt(1) // 1 Math.cbrt(2) // 1.2599210498948734對于非數值, Math.cbrt 方法內部也是先使用 Number 方法將其轉為數值
Math.cbrt('8') // 2 Math.cbrt('hello') // NaN對于沒有部署這個方法的環境,可以用下面的代碼模擬。
Math.cbrt = Math.cbrt || function(x) { var y = Math.pow(Math.abs(x), 1/3); return x < 0 ? -y : y; };7.4Math.clz32()
JavaScript 的整數使用 32 位二進制形式表示, Math.clz32 方法返回一個數的 32 位無符號整數形式有多少個前導 0。
Math.clz32(0) // 32 Math.clz32(1) // 31 Math.clz32(1000) // 22 Math.clz32(0b01000000000000000000000000000000) // 1 Math.clz32(0b00100000000000000000000000000000) // 2上面代碼中,0 的二進制形式全為 0,所以有 32 個前導 0;1 的二進制形式是 0b1 ,只占 1 位,所以 32 位之中有 31 個前導 0;1000 的二進制形式
是 0b1111101000 ,一共有 10 位,所以 32 位之中有 22 個前導 0。
clz32 這個函數名就來自”count leading zero bits in 32-bit binary representation of a number“(計算一個數的 32 位二進制形式的前導 0 的個
數)的縮寫。
左移運算符( << )與 Math.clz32 方法直接相關。
對于小數, Math.clz32 方法只考慮整數部分。
Math.clz32(3.2) // 30 Math.clz32(3.9) // 30對于空值或其他類型的值, Math.clz32 方法會將它們先轉為數值,然后再計算。
Math.clz32() // 32 Math.clz32(NaN) // 32 Math.clz32(Infinity) // 32 Math.clz32(null) // 32 Math.clz32('foo') // 32 Math.clz32([]) // 32 Math.clz32({}) // 32 Math.clz32(true) // 317.5Math.imul()
Math.imul 方法返回兩個數以 32 位帶符號整數形式相乘的結果,返回的也是一個 32 位的帶符號整數。
Math.imul(2, 4) // 8 Math.imul(-1, 8) // -8 Math.imul(-2, -2) // 4如果只考慮最后 32 位,大多數情況下, Math.imul(a, b) 與 a * b 的結果是相同的,即該方法等同于 (a * b)|0 的效果(超過 32 位的部分溢出)。之
所以需要部署這個方法,是因為 JavaScript 有精度限制,超過 2 的 53 次方的值無法精確表示。這就是說,對于那些很大的數的乘法,低位數值往往都
是不精確的, Math.imul 方法可以返回正確的低位數值
(0x7fffffff * 0x7fffffff)|0 // 0上面這個乘法算式,返回結果為 0。但是由于這兩個二進制數的最低位都是 1,所以這個結果肯定是不正確的,因為根據二進制乘法,計算結果的二進制
最低位應該也是 1。這個錯誤就是因為它們的乘積超過了 2 的 53 次方,JavaScript 無法保存額外的精度,就把低位的值都變成了 0。 Math.imul 方法可
以返回正確的值 1
?
7.6Math.fround()
Math.fround 方法返回一個數的單精度浮點數形式。
Math.fround(0) // 0 Math.fround(1) // 1 Math.fround(1.337) // 1.3370000123977661 Math.fround(1.5) // 1.5 Math.fround(NaN) // NaN對于整數來說, Math.fround 方法返回結果不會有任何不同,區別主要是那些無法用 64 個二進制位精確表示的小數。這時, Math.fround 方法會返回最
接近這個小數的單精度浮點數。
對于沒有部署這個方法的環境,可以用下面的代碼模擬
7.7Math.hypot()
Math.hypot 方法返回所有參數的平方和的平方根
Math.hypot(3, 4); // 5 Math.hypot(3, 4, 5); // 7.0710678118654755 Math.hypot(); // 0 Math.hypot(NaN); // NaN Math.hypot(3, 4, 'foo'); // NaN Math.hypot(3, 4, '5'); // 7.0710678118654755 Math.hypot(-3); // 3上面代碼中,3 的平方加上 4 的平方,等于 5 的平方。
如果參數不是數值, Math.hypot 方法會將其轉為數值。只要有一個參數無法轉為數值,就會返回 NaN。
8對數方法
ES6 新增了 4 個對數相關方法
8.1Math.expm1()
Math.expm1(x) 返回 e - 1,即 Math.exp(x) - 1 。 Math.expm1(-1) // -0.6321205588285577 Math.expm1(0) // 0 Math.expm1(1) // 1.718281828459045對于沒有部署這個方法的環境,可以用下面的代碼模擬。
Math.expm1 = Math.expm1 || function(x) { return Math.exp(x) - 1; };8.2Math.log1p()
Math.log1p(x) 方法返回 1 + x 的自然對數,即 Math.log(1 + x) 。如果 x 小于-1,返回 NaN
Math.log1p(1) // 0.6931471805599453 Math.log1p(0) // 0 Math.log1p(-1) // -Infinity Math.log1p(-2) // NaN對于沒有部署這個方法的環境,可以用下面的代碼模擬。
Math.log1p = Math.log1p || function(x) { return Math.log(1 + x); };8.3Math.log10()
Math.log10(x) 返回以 10 為底的 x 的對數。如果 x 小于 0,則返回 NaN。
Math.log10(2) // 0.3010299956639812 Math.log10(1) // 0 Math.log10(0) // -Infinity Math.log10(-2) // NaN Math.log10(100000) // 5對于沒有部署這個方法的環境,可以用下面的代碼模擬
Math.log10 = Math.log10 || function(x) { return Math.log(x) / Math.LN10; };8.4Math.log2()
Math.log2(x) 返回以 2 為底的 x 的對數。如果 x 小于 0,則返回 NaN。
Math.log2(3) // 1.584962500721156 Math.log2(2) // 1 Math.log2(1) // 0 Math.log2(0) // -Infinity Math.log2(-2) // NaN Math.log2(1024) // 10 Math.log2(1 << 29) // 29對于沒有部署這個方法的環境,可以用下面的代碼模擬。
Math.log2 = Math.log2 || function(x) { return Math.log(x) / Math.LN2; };9雙曲函數方法
ES6 新增了 6 個雙曲函數方法。
Math.sinh(x) 返回 x 的雙曲正弦(hyperbolic sine)
Math.cosh(x) 返回 x 的雙曲余弦(hyperbolic cosine)
Math.tanh(x) 返回 x 的雙曲正切(hyperbolic tangent)
Math.asinh(x) 返回 x 的反雙曲正弦(inverse hyperbolic sine)
Math.acosh(x) 返回 x 的反雙曲余弦(inverse hyperbolic cosine)
Math.atanh(x) 返回 x 的反雙曲正切(inverse hyperbolic tangent)
10. Math.signbit()
Math.sign() 用來判斷一個值的正負,但是如果參數是 -0 ,它會返回 -0 。
Math.sign(-0) // -0這導致對于判斷符號位的正負, Math.sign() 不是很有用。JavaScript 內部使用 64 位浮點數(國際標準 IEEE 754)表示數值,IEEE 754 規定第一位
是符號位, 0 表示正數, 1 表示負數。所以會有兩種零, +0 是符號位為 0 時的零值, -0 是符號位為 1 時的零值。實際編程中,判斷一個值是 +0 還是 -0 非
常麻煩,因為它們是相等的。
目前,有一個提案,引入了 Math.signbit() 方法判斷一個數的符號位是否設置了
Math.signbit(2) //false Math.signbit(-2) //true Math.signbit(0) //false Math.signbit(-0) //true可以看到,該方法正確返回了 -0 的符號位是設置了的。
該方法的算法如下。
如果參數是 NaN ,返回 false
如果參數是 -0 ,返回 true
如果參數是負值,返回 true
其他情況返回 false
11. 指數運算符
ES2016 新增了一個指數運算符( ** )。
2 ** 2 // 4 2 ** 3 // 8指數運算符可以與等號結合,形成一個新的賦值運算符( **= )
let a = 1.5; a **= 2; // 等同于 a = a * a; let b = 4; b **= 3; // 等同于 b = b * b * b;注意,在 V8 引擎中,指數運算符與 Math.pow 的實現不相同,對于特別大的運算結果,兩者會有細微的差異。
Math.pow(99, 99) // 3.697296376497263e+197 99 ** 99 // 3.697296376497268e+197上面代碼中,兩個運算結果的最后一位有效數字是有差異的
12Integer 數據類型
12.1簡介
JavaScript 所有數字都保存成 64 位浮點數,這決定了整數的精確程度只能到 53 個二進制位。大于這個范圍的整數,JavaScript 是無法精確表示的,
這使得 JavaScript 不適合進行科學和金融方面的精確計算。
現在有一個提案,引入了新的數據類型 Integer(整數),來解決這個問題。整數類型的數據只用來表示整數,沒有位數的限制,任何位數的整數都可以
精確表示。
為了與 Number 類型區別,Integer 類型的數據必須使用后綴 n 表示
typeof 運算符對于 Integer 類型的數據返回 integer
typeof 123n // 'integerJavaScript 原生提供 Integer 對象,用來生成 Integer 類型的數值。轉換規則基本與 Number() 一致
Integer(123) // 123n Integer('123') // 123n Integer(false) // 0n Integer(true) // 1n以下的用法會報錯。
new Integer() // TypeError Integer(undefined) //TypeError Integer(null) // TypeError Integer('123n') // SyntaxError Integer('abc') // SyntaxError12.2運算
在數學運算方面,Integer 類型的 + 、 - 、 * 和 ** 這四個二元運算符,與 Number 類型的行為一致。除法運算 / 會舍去小數部分,返回一個整數。
9n / 5n // 1n幾乎所有的 Number 運算符都可以用在 Integer,但是有兩個除外:不帶符號的右移位運算符 >>> 和一元的求正運算符 + ,使用時會報錯。前者是因為
>>> 要求最高位補 0,但是 Integer 類型沒有最高位,導致這個運算符無意義。后者是因為一元運算符 + 在 asm.js 里面總是返回 Number 類型或者報
錯。
Integer 類型不能與 Number 類型進行混合運算
這是因為無論返回的是 Integer 或 Number,都會導致丟失信息。比如 (2n**53n + 1n) + 0.5 這個表達式,如果返回 Integer 類型, 0.5 這個小數部分
會丟失;如果返回 Number 類型,會超過 53 位精確數字,精度下降。
相等運算符( == )會改變數據類型,也是不允許混合使用
精確相等運算符( === )不會改變數據類型,因此可以混合使用。
0n === 0 // false總結
本博客源于本人閱讀相關書籍和視頻總結,創作不易,謝謝點贊支持。學到就是賺到。我是歌謠,勵志成為一名優秀的技術革新人員。
歡迎私信交流,一起學習,一起成長。
推薦鏈接 其他文件目錄參照
“睡服“面試官系列之各系列目錄匯總(建議學習收藏)
總結
以上是生活随笔為你收集整理的“睡服”面试官系列第九篇之数值的扩展(建议收藏学习)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 移动通信原理,GSM/GPRS模块无线传
- 下一篇: 操作系统课程设计:模拟文件系统