1. 程式人生 > >javascript型別系統——Number數字型別

javascript型別系統——Number數字型別

前面的話

  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.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"

參考資料