前面的話
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()
文章列表