前面的話
javascript只有一種數字型別,它在內部被表示為64位的浮點數,和java的double數字型別一樣。與其他大多數程式語言不同的是,它沒有分離出整數型別,所以1和1.0的值相同。這提供了很大的方便,避免了一大堆因數字型別導致的錯誤
數字Number是javascript中基本的原始資料型別,同時javascript也支援Number物件,它是一個原始數值的包裝物件。在需要時,javascript會自動在原始形式和物件形式之間轉換。本文將介紹數字Number原始型別及Number包裝物件
定義
javascript採用IEEE754格式來表示數字,不區分整數和浮點數,javascript中的所有數字都用浮點數值表示
由於浮點型數值需要的記憶體空間是儲存整數值的兩倍,因此javascript會不失時機地將浮點數值轉換成整數值,若小數點後沒有跟任何數字或者浮點值本身表示的就是一個整數,這個數值會作為整數值來儲存
console.log(1.0,1.0===1);//1 true console.log(1.,1.===1);//1 true
當一個數字直接出現在javascript程式中時,稱之為數字字面量(numeric literal)。而當Number()使用new操作符用做建構函式時,稱之為Number物件
整數
javascript的整數表示共有四種字面量格式是十進位制、二進位制、八進位制、十六進位制。但在進行算術計算時,所有以二進位制、八進位制和十六進位制表示的數值最終都將被轉換成十進位制數值
【1】八進位制字面值的第一位必須是0,然後是八進位制數字序列(0-7)。如果字面值中的數值超出了範圍,那麼前導0將被忽略,後面的數值被當作十進位制數解析
[注意]由於某些javascript的實現不支援八進位制字面量,且八進位制字面量在嚴格模式下是無效的,會導致javascript丟擲錯誤。所以儘量不使用八進位制字面量
【2】十六進位制字面值的前兩位必須是0x,後跟十六進位制數字序列(0-9,a-f),字母可大寫可小寫。如果十六進位制中字面值中的數值超出範圍,如出現g、h等會報錯
【3】二進位制字面值的前兩位必須是0b,如果出現除0、1以外的數字會報錯
var num2 = 0b101; console.log(num2);//5 var num2 = 0b2; console.log(num2);//報錯 var num8 = 012; console.log(num8);//10 var num8 = 09; console.log(num8);//9 var num16 = 0x11; console.log(num16);//17 var num16 = 0xg; console.log(num16);//報錯
浮點數
浮點數(floating-point number)是指數值中必須包含一個小數點,且小數點後面必須至少有一位數字。與整數支援多進位制不同,一般地,浮點數只可用十進位制表示
var num1 = 011.1;//報錯 var num2 = 0x11.1;//報錯 var num3 = 011e1;//報錯 var num4 = 0x11e1;//出錯,會被識別成整數,結果為4577
[注意]雖然小數點前面可以沒有整數,但不推薦
var num1 = 1.1; var num2 = 1.; var num3 = .1; console.log(num1,num2,num3);//1.1,1,0.1
由於javascript採用IEEE754格式表示數字,浮點數不是精確值,所以涉及浮點數的比較和運算時要特別小心
console.log(0.1 + 0.2 === 0.3);// false console.log(0.3 / 0.1);// 2.9999999999999996 console.log((0.3 - 0.2) === (0.2 - 0.1));// false
科學記數法
對於極大或者極小的數,可以用科學計數法e來表示的浮點數值來表示。科學計數法允許字母e或E的後面,跟著一個整數,表示這個數值的指數部分
以下兩種情況,javascript會自動將數值轉為科學計數法表示
【1】小於1且小數點後面帶有6個0以上的浮點數值
0.0000003 // 3e-7 0.000003 // 0.000003
【2】整數位數字多於21位
1234567890123456789012 //1.2345678901234568e+21 1234567890123456789012.1 //1.2345678901234568e+21 123456789012345678901 //123456789012345680000
數值精度
根據國際標準IEEE 754,javascript浮點數的64個二進位制位,從最左邊開始,是這樣組成的
第1位: 符號位,0表示正數,1表示負數
第2位到第12位: 儲存指數部分
第13位到第64位:儲存小數部分(即有效數字)
符號位決定了一個數的正負,指數部分決定了數值的大小,小數部分決定了數值的精度
IEEE 754規定,有效數字第一位預設總是1,不儲存在64位浮點數之中。也就是說,有效數字總是1.xx...xx的形式,其中xx..xx的部分儲存在64位浮點數之中,最長可能為52位
因此,javascript提供的有效數字最長為53個二進位制位
//javascript內部實際的表現形式 (-1)^符號位 * 1.xx...xx * 2^指數位
精度最長為53個二進位制位,意味著絕對值小於2的53次方的整數,即-(253-1)到253-1,都可以精確表示
Math.pow(2, 53) // 9007199254740992
所以換算成十進位制,javascript數字最高精度是16位(若整數部分為0,則表示小數點後16位;若整數部分不為0,則表示整體保留16位)
Math.pow(2, 53)// 9007199254740992 Math.pow(2, 53) + 1// 9007199254740992 9007199254740993//9007199254740992 90071992547409921//90071992547409920 0.923456789012345678;//0.9234567890123456 9.23456789012345678;//9.234567890123456
數值範圍
根據標準,64位浮點數的指數部分的長度是11個二進位制位,意味著指數部分的最大值是2047(211-1)。分出一半表示負數,則javascript能夠表示的數值範圍為21024到2-1023,超出這個範圍的數無法表示
21024 = 1.79769*10308
javascript中能表示的最大值是+-1.79769*10308,而javascript能表示的最小值是+-5*10-324
javascript能夠表示的整數範圍是-253到253。如果超過了此範圍的整數,無法保證低位數字的精度
javascript中的最大值儲存在Number.MAX_VALUE中,而最小值儲存在Number.MIN_VALUE
console.log(Number.MIN_VALUE,Number.MAX_VALUE)//5e-324,1.7976931348623157e+308
如果數字超過最大值,javascript會返回Infinity,這稱為正向溢位(overflow);如果等於或超過最小負值-1023(即非常接近0),javascript會直接把這個數轉為0,這稱為負向溢位(underflow)
如下所示,實際情況並非全部如此
Number.MAX_VALUE+1 === Number.MAX_VALUE;//true
當數字最大值+1時,結果並不等於Infinity,而是仍然等於最大值。這是因為精度受限,javascript中的儲存位置沒有多餘位置去儲存個位數1
當運算數和數字最大值保持在相同精度維度上時,才可與數字最大值發生運算
Number.MAX_VALUE+1e291;//1.7976931348623157e+308 Number.MAX_VALUE+1e292;//Infinity
類似地,與數字最小值的運算也有相似情況
Number.MIN_VALUE + 1;//1 Number.MIN_VALUE - 3e-324;//0 Number.MIN_VALUE - 2e-324;//5e-324
0.1+0.2!== 0.3
不僅僅是javascript,在很多語言中0.1 + 0.2都會得到0.30000000000000004
下面詳細解釋出現這個結果的原因
[注意]該部分內容主要參考子遲兄的博文【0.1 + 0.2 = 0.30000000000000004】該怎樣理解?
計算機中的數字都是以二進位制儲存的,如果要計算0.1 + 0.2 的結果,計算機會先把0.1和0.2分別轉化成二進位制,然後相加,最後再把相加得到的結果轉為十進位制
把10進位制的0.1轉換成2進位制,表示為0.0 0011 0011...(0011迴圈)
(0.1).toString(2);//"0.0001100110011001100110011001100110011001100110011001101"
把10進位制的0.2轉換成2進位制,表示為0.0011 0011...(0011迴圈)
(0.2).toString(2);//"0.001100110011001100110011001100110011001100110011001101"
由於計算機只能儲存最大53位精度,所以,用科學記數法表示
0.1的二進位制為1.1001100110011001100110011001100110011001100110011010e+4(52位小數)
0.2的二進位制為1.1001100110011001100110011001100110011001100110011010e+3(52位小數)
[注意]如果第52bit和53bit都是 1,需要進位
1.1001100110011001100110011001100110011001100110011010e-4 + 1.1001100110011001100110011001100110011001100110011010e-3 -------------------------------------------------------------------------- 0.1100110011001100110011001100110011001100110011001101e-3 + 1.1001100110011001100110011001100110011001100110011010e-3 -------------------------------------------------------------------------- 10.0110011001100110011001100110011001100110011001100111e-3 -------------------------------------------------------------------------- 1.0011001100110011001100110011001100110011001100110100e-2(52位小數) -------------------------------------------------------------------------- 0.010011001100110011001100110011001100110011001100110100 轉換為十進位制為0.30000000000000004
特殊數值
javascript提供了幾個特殊數值,包括Number.MAX_VALUE、Number.MIN_VALUE、Number.POSITIVE_INFINITY、Number.NEGATIVE_INFINITY、Number.MAX_SAFE_INTEGER、Number.MIN_SAFE_INTEGER、Number.NaN、+0、-0共9個
其中,前7個特殊數值是Number物件的屬性
最值
前面已介紹過Number.MAX_VALUE代表javascript最大值,Number.MIN_VALUE代表javascript最小正值
console.log(Number.MIN_VALUE,Number.MAX_VALUE)//5e-324,1.7976931348623157e+308
Number.MAX_SAFE_INTEGER表示最大整數(253-1),Number.MIN_SAFE_INTEGER表示最小整數-(253-1)
//9007199254740991 true console.log(Number.MAX_SAFE_INTEGER,Number.MAX_SAFE_INTEGER===Math.pow(2, 53)-1) //-9007199254740991 true console.log(Number.MIN_SAFE_INTEGER,Number.MIN_SAFE_INTEGER===-(Math.pow(2, 53)-1))
Infinity
Infinity是一個全域性屬性,用來存放表示無窮大的特殊數值。用for/in迴圈不可列舉Infinity屬性,用delete操作符也無法刪除它
實際上,Number.POSITIVE_INFINITY對應的是Infinity,代表正無窮;而Number.NEGATIVE_INFINITY對應的是-Infinity,代表負無窮
console.log(Number.POSITIVE_INFINITY,Number.NEGATIVE_INFINITY);//Infinity -Infinity
Infinity有正負之分
Math.pow(2,Math.pow(2,100));//Infinity 1/0;//Infinity -0/0;//-Infinity Infinity === -Infinity;//false
Infinity參與的運算結果只能是其本身、0或NaN
2 * Infinity;//Infinity 2 - Infinity;//-Infinity 2 + Infinity;//Infinity 2 / Infinity;//0 Infinity / 2;//Infinity
Infinity * Infinity;//Infinity Infinity - Infinity;//NaN Infinity + Infinity;//Infinity Infinity / Infinity;//NaN
可以通過isFinite()來確定一個數值是不是有窮的,包含著隱式型別轉換Number()。如果是+-Infinity或NaN時返回false,否則為true
console.log(isFinite(Infinity))//false console.log(isFinite(NaN))//false console.log(isFinite(Number.MAX_VALUE))//true console.log(isFinite(true))//true
NaN
NaN(not a number)表示非數字,NaN與任何值都不相等,包括NaN本身,且任何涉及NaN的操作都會返回NaN
5 - 'x'; //NaN Math.acos(2); //NaN 0 / 0; //NaN
NaN == NaN;//false NaN == Infinity;//false
[NaN].indexOf(NaN);// -1 Boolean(NaN); // false
isNaN()來判斷這個數字是不是NaN,包含著隱式型別轉換Number()
console.log(isNaN(Infinity));//false console.log(isNaN(0));//false console.log(isNaN(NaN));//true console.log(isNaN('Hello'));//true
判斷NaN更可靠的方法是,利用NaN是javascript之中唯一不等於自身的值這個特點,進行判斷
function myIsNaN(value) { return value !== value; }
正負0
在javascript內部,實際上存在2個0:一個是+0,一個是-0。它們是等價的
-0 === +0;// true 0 === -0;// true 0 === +0;// true
一般地,+0和-0都會被當做0來看待,但是+0或-0當作分母,返回的值是不相等的
console.log(1/+0);//Infinity console.log(1/-0);//-Infinity console.log((1/+0) === (1/-0));//false
轉成數值
有3個函式可以把非數值轉換成數值:Number()、parseInt()和parseFloat()。其中Number()可以將任意型別的值轉化成數值,而parseInt()和parseFloat()只應用於字串向數字的轉換
Number()
當把Number()當作一個函式來呼叫,而不是作為構造器,它執行一個型別轉換。使用Number()函式可以將任意型別的值轉化成數值
// 數值:十進位制數字 console.log(Number(11),Number(011),Number(0x11));//11 9 17 // undefined:轉成 NaN Number(undefined) // NaN // null:轉成0 Number(null) // 0 // 布林值:true 轉成1,false 轉成0 console.log(Number(true),Number(false));//1 0
Number()函式解析字串時會識別出字串的前置空格並去掉
【1】若字串只包含十進位制或十六進位制數字,則轉成十進位制的數字
[注意1]Number()不識別八進位制數字的字串,會按照十進位制數字處理
[注意2]字串'1.2.'不會報錯,但數字1.2.會報錯
【2】若字串為空字串或空格字串,則轉成0
【3】其他情況的字串,則轉成NaN
console.log(Number(' 123'));//123 console.log(Number('1.2.'));//NaN console.log(Number(1.2.));//報錯 console.log(Number(''),Number(' '));//0 0 console.log(Number('11'),Number('011'),Number('0x11'));//11 11 17 console.log(Number('abc'));//NaN console.log(Number('123abc'));//NaN
Number()函式解析物件時,會按照以下步驟進行處理
【1】呼叫物件的valueOf()方法,如果返回原始型別的值,則直接對該值使用Number()函式
【2】如果valueOf()方法返回的還是物件,則呼叫物件的toString()方法,如果返回原始型別的值,則對該值使用Number()函式
【3】如果toString()方法返回的依然是物件,則結果是NaN
在第一步中,由於只有時間Date()物件返回的是原始型別的值數字,所以Number(new Date())返回現在到1970年1月1日00:00:00的數值型別的毫秒數
Number(new Date())//1465976459108
在第二步中,陣列Array型別返回由陣列中每個值的字串形式拼接而成的一個以逗號分隔的字串,如果字串中只存在數字,則返回數字,其他情況返回NaN;由於其他物件的toString()方法返回的字串中不只包括數字,所以返回NaN
Number([]);//0 Number([0]);//0 Number([-0]);//0 Number([10]);//10 Number([1,2]);//NaN Number(其他物件);//NaN
parseInt()
【1】parseInt()專門用於把字串轉換成整數。在轉換字串時,會忽略字串前面的空格,直到找到第一個非空格字元。如果第一個字元不是數字字元或者負號,parseInt()就會返回NaN。如果是,則繼續解析,直到解析完成或者遇到非數字字元
console.log(parseInt(' 123.8px'));//123 console.log(parseInt(' 123.8 '));//123 console.log(parseInt(' -123.8px'));//-123 console.log(parseInt('a123.8px'));//NaN console.log(parseInt('0 123.8px'));//0
【2】parseInt()可以識別出各種進位制的數字,輸出的是運算後的十進位制的數字,如1.0或1.或01會以1輸出。在解析八進位制字面量的字串,ECMAScript3會解析八進位制,但ECMAScript5沒有解析八進位制的能力
console.log(parseInt('11'));//11 console.log(parseInt(11));//11 console.log(parseInt('11.1'));//11 console.log(parseInt(11.1));//11 console.log(parseInt('011'));//11 console.log(parseInt(011));//9 console.log(parseInt('011.1'));//11 console.log(parseInt(011.1));//報錯 console.log(parseInt('0x11'));//17 console.log(parseInt(0x11));//17 console.log(parseInt('0x11.1'));//17 console.log(parseInt(0x11.1));//報錯
[注意]對於那些會自動轉為科學計數法的數字,parseInt會將科學計數法的表示方法視為字串,因此導致一些奇怪的結果
console.log(parseInt(1000000000000000000000.5)); // 1 // 等同於 console.log(parseInt('1e+21')); // 1 console.log(parseInt(0.0000008)); // 8 // 等同於 console.log(parseInt('8e-7')); // 8
【3】parseInt()
方法還可以接受第二個引數(2到36之間),表示被解析的值的進位制,返回該值對應的十進位制數。預設情況下,parseInt
的第二個引數為10,即預設是十進位制轉十進位制
console.log(parseInt('11',2));//3 console.log(parseInt('11',8));//9 console.log(parseInt('11',10));//11 console.log(parseInt('11',16));//17
如果第二個引數不是數值,會被自動轉為一個整數。這個整數只有在2到36之間,才能得到有意義的結果,超出這個範圍,則返回NaN。如果第二個引數是0、undefined和null,則直接忽略
console.log(parseInt('10', 37)); // NaN console.log(parseInt('10', 1)); // NaN console.log(parseInt('10', 0)); // 10 console.log(parseInt('10', null)); // 10 console.log(parseInt('10', undefined)); // 10
如果字串包含對於指定進位制無意義的字元,則從最高位開始,只返回可以轉換的數值。如果最高位無法轉換,則直接返回NaN
console.log(parseInt('1546', 2)); // 1 console.log(parseInt('546', 2)); // NaN
【4】parseInt()是專門用來處理字串轉換數字的,parseInt處理非字串和數字型別時輸出NaN。但是,實際上parseInt()包含著隱式的toString()方法,所以parseInt([數字或字串])輸出對應的數字
console.log(parseInt(null),parseInt(undefined));//NaN NaN console.log(parseInt(true),parseInt(false));//NaN NaN console.log(parseInt([]),parseInt(['2.5px']),parseInt([2.5]));//NaN 2 2 console.log(parseInt(''),parseInt(' '),parseInt({}));//NaN NaN NaN
parseFloat()
【1】parseFloat()專門用於字串轉換浮點數。同樣地,解析時會忽略字串前面的空格,直到找到第一個非空格字元,然後一直解析到字串末尾或一個無效的浮點數字字元為止
console.log(parseFloat(' 0123.px'));//123 console.log(parseFloat(' 123.px'));//123 console.log(parseFloat(' 123.1px'));//123.1 console.log(parseFloat(' 123.1.2px '));//123.1 console.log(parseFloat(' -123.0px'));//-123 console.log(parseFloat('.123.1px'));//0.123 console.log(parseFloat('0 123px'));//0
[注意]如果字串符合科學計數法,則會進行相應的轉換
console.log(parseFloat('314e-2')); // 3.14 console.log(parseFloat('0.0314E+2')); // 3.14
【2】parseFloat()可以識別不同進位制的數字,但只能解析十進位制字串
console.log(parseFloat('11'));//11 console.log(parseFloat(11));//11 console.log(parseFloat('11.1'));//11.1 console.log(parseFloat(11.1));//11.1 console.log(parseFloat('011'));//11 console.log(parseFloat(011));//9 console.log(parseFloat('011.1'));//11.1 console.log(parseFloat(011.1));//報錯 console.log(parseFloat('0x11'));//0 console.log(parseFloat(0x11));//17 console.log(parseFloat('0x11.1'));//0 console.log(parseFloat(0x11.1));//報錯
【3】parseFloat()是專門用來處理字串轉換浮點數的,parseFloat處理非字串和數字型別時輸出NaN。但是,實際上parseFloat()包含著隱式的toString()方法,所以parseFloat([數字或字串])輸出對應的數字
console.log(parseFloat(null),parseFloat(undefined));//NaN NaN console.log(parseFloat(true),parseFloat(false));//NaN NaN console.log(parseFloat([]),parseFloat([2.1]),parseFloat(['2.1px']));//NaN 2.1 2.1 console.log(parseFloat(''),parseFloat({}));//NaN NaN
[注意]Number('')的結果是0,parseInt('')和parseFloat('')的結果是NaN
例項方法
關於Number()物件的例項方法總共有6個,分為兩類。包括toString()、toLocalString()、valueOf()這3種物件通用方法和toFixed()、toExponential()、toPrecision()這3種改變數值顯示形式並轉換為字串的方法
valueOf()方法返回物件的數字字面量
toString()方法將數字轉換為字串
toLocalString()方法將數字轉換為本地慣例格式化數字的字串
console.log(typeof 1.1.valueOf(),1.1.valueOf());//number 1.1 console.log(typeof 1.1.toString(),1.1.toString());//String '1.1' console.log(typeof 1.1.toLocaleString(),1.1.toLocaleString());//String '1.1'
[注意]如果數字不加括號,點會被javascript引擎解釋成小數點,從而報錯
console.log(typeof 1.valueOf(),1.valueOf());//報錯 console.log(typeof 1.toString(),1.toString());//報錯 console.log(typeof 1.toLocaleString(),1.toLocaleString());//報錯
console.log(typeof (1).valueOf(),(1).valueOf());//number 1 console.log(typeof (1).toString(),(1).toString());//String '1' console.log(typeof (1).toLocaleString(),(1).toLocaleString());//String '1'
除了為數字加上括號,還可以在數字後面加兩個點,javascript會把第一個點理解成小數點,把第二個點理解成呼叫物件屬性,從而得到正確結果
console.log(10..toString()); // "10" console.log(10 .toString()); // "10" console.log(10.0.toString()); // "10"
toString()方法可以接受一個引數,該引數應當是2到36之間的整數,表示輸出的進位制。如果該引數不存在,或者為undefined,預設將數值先轉為十進位制,再輸出字串
var num = 10; console.log(num.toString());//'10' console.log(num.toString(2));//'1010' console.log(num.toString(8));//'12' console.log(num.toString(10));//'10' console.log(num.toString(16));//'a' console.log(num.toString(undefined));//'10'
如果引數超出2-36的範圍,或者為其他值時,報錯
console.log((10).toString(0));//報錯 console.log((10).toString(null));//報錯
toFixed()
toFixed()方法按照指定的小數位返回數值四捨五入後的字串表示(常用於處理貨幣值)
[注意]toFixed()裡的引數只接受0-20,若不傳參或引數為undefined則相當於引數是0
var num = 10.456; console.log(num.toFixed(2));//'10.46' console.log(num.toFixed());//'10' console.log(num.toFixed(0));//'10' console.log(num.toFixed(undefined));//'10' console.log(num.toFixed(-1));//報錯
toExponential()
toExponential()方法返回數值四捨五入後的指數表示法(e表示法)的字串表示,參數列示轉換後的小數位數
[注意]toExponential()方法裡的引數只接受0-20,但與toFxied()不同的是,若不傳參或引數為undefined,則保留儘可能多的有效數字;若引數是0表示沒有小數部分
var num = 10.456; console.log(num.toExponential(2));//'1.05e+1' console.log(num.toExponential());//'1.0456e+1' console.log(num.toExponential(0));//'1e+1' console.log(num.toExponential(undefined));//'1.0456e+1' console.log(num.toExponential(-1));//報錯
toPrecision()
toPrecision()方法接收一個引數,即表示數值的所有數字的位數(不包括指數部分),自動呼叫toFixed()或toExponential()
[注意]toPrecision()裡的引數只接受1-21,若不傳參或引數為undefined則相當於呼叫toString()方法
var num = 10.1; console.log(num.toPrecision(3));//'10.1' console.log(num.toPrecision(2));//'10' console.log(num.toPrecision(1));//'1e+1' console.log(num.toPrecision());//'10.1' console.log(num.toPrecision(undefined));//'10.1' console.log(num.toPrecision(0));//報錯
[注意]toFixed()、toExponential()、toPrecision()這三個方法在小數位用於四捨五入時都不太可靠,跟浮點數不是精確儲存有關
console.log((12.25).toPrecision(3)); // "12.3" console.log((12.25).toFixed(1)); // "12.3" console.log((12.25).toExponential(2)); // "1.23e+1" console.log((12.35).toPrecision(3)); // "12.3" console.log((12.35).toFixed(1)); // "12.3" console.log((12.35).toExponential(2)); // "1.23e+1"
參考資料
【1】 ES5/型別 https://www.w3.org/html/ig/zh/wiki/ES5/types#Number_.E7.B1.BB.E5.9E.8B ES5/ ES5/標準內建物件 https://www.w3.org/html/ig/zh/wiki/ES5/builtins#Number_.E5.AF.B9.E8.B1.A1
【2】 阮一峰Javascript標準參考教程——基本語法之數值http://javascript.ruanyifeng.com/grammar/number.html 標準庫Number物件http://javascript.ruanyifeng.com/stdlib/number.html
【3】 W3School-Javascript高階教程——ECMAScript原始型別 http://www.w3school.com.cn/js/pro_js_primitivetypes.asp
【4】《javascript權威指南(第6版)》第3章 型別、值和變數
【5】《javascript高階程式設計(第3版)》第3章 基本概念 第5章 引用型別
【6】《javascript語言精粹(修訂版)》第2章 語法 第8章 方法
【7】《javascript DOM程式設計藝術(第2版)》第2章 Javascript語法
【8】《javascript啟示錄》 第11章 Number()