JavaScript數值問題

來源:互聯網
上載者:User
如果你是一個Web開發人員,應該也寫過這種接受使用者輸入的JavaScript代碼:


<input type="text" name="age" onchange="return (this.value>0)">

表面上看,這行代碼不存在任何問題,但實際運行一段時間後,卻發現它偶爾莫名其妙地罷工。

這行代碼的問題是,使用者不一定輸入標準的年齡整數,他可能輸入“23.5”,也可能輸入“二十三”。改正的辦法是加入一些轉換過程,例如:


<input type="text" name="age" onchange="return parseFloat( this.value)">

JavaScript是一種指令碼語言,它的設計目標之一就是儘可能地簡化使用,所以它處理字串和數值的方式極其靈活,許多操作都是隱含執行、不需要開發人員幹預。那麼,它到底靈活到哪種程度?高度靈活和高度自動化的底下是否隱藏著陷阱?是否可以定義擁有3.5個元素的數組?1加上1000000是否一定等於1000001,而不是等於1.000E6?如何保證這個數值正確顯示?

我們知道以前的許多瀏覽器存在數值處理方面的BUG,現在是否可以高枕無憂,或者仍然必須小心翼翼?本文為你介紹指令碼引擎的數值處理限制,主要針對目前流行的瀏覽器,包括5.x以上的IE、4.7x以上的Netscape、1.x的Mozilla。讀完本文,你將知道在瀏覽器中執行哪些數值操作是安全的,哪些操作是不安全的。

一種解釋:科學就是描述現實世界

從表面上看,數值是一個很容易理解的概念。最常用的數實值型別是整數和實數,想必你一定已經熟悉它們了。簡單的例子如2和23.45;但也有複雜的例子,如互連網最大梅森素數搜尋的結果已超過4百萬位元字。我們知道PI(圓周率)是一個無限不迴圈小數,如何寫出比3.141592654精度更高的PI值?一言蔽之,你根本辦不到。

無論是用紙張記錄數值,還是用電腦記錄數值,都必須用某種編碼方案來表達數值。必須理解的是,用編碼錶達的數值不是數值本身,而只是數值的一種人類或電腦可理解的描述。任何編碼方案都有其局限,要麼是表達範圍(精度)方面的限制,要麼是其他複雜性方面的制約。例如,“2”究竟是一個整數還是實數?或者兩者都是?

從數學課上,我們知道迴圈的數值可以用一個句點表示,例如我們不必寫1.9999……(後面加上無數個9),只要寫1.9再在9上面加一個點。另外我們知道,“1點9的迴圈”實際上等於2,因為1+(0.90.09+0.009+…)收斂的極限就是2。也就是說,同一個數值“2”有兩種表達方法。如果算上2.0000,那就有三種表達方法。所以說,並非每一個數值都只有唯一的表達方式,即使在紙上寫也一樣!這樣一來,處理數值就很不方便、甚至難以令人接受了!

二套標準:化繁為簡的折衷方案

科學理論固然美妙,不過現實生活中不可能(也沒有必要)嚴格堅持科學理論的精密。怎麼辦呢?訂立工程標準。例如,IEEE 754標準《二進位浮點數演算法》(www.ieee.org)就是一個對實數進行電腦編碼的標準。

JavaScript的數值處理就建立在這類標準之上。按照ECMA的JavaScript標準,它的Number類型就是IEEE 754的雙精確度數值。絕對完美的數值編碼方案是不存在的,為了處理方便,這個標準引入了大量的折衷和妥協,建立在這種表達方式上的演算法(例如除法運算)也一樣。由於數值表達方式存在“缺陷”,運算結果不可避免地堆聚起越來越多的誤差——大多數情況下,這類誤差不至於引起問題,但這並不意味著你可以完全忽視,請看下面兩個例子:

⑴ IEEE 754除法錯誤曾經使Intel的Pentium付出昂貴的代價,後來Intel不得不為發布出去的晶片BUG公開道歉,累計損失約5億美元。

⑵ 日常的利息計算不可避免地涉及浮點運算,沒有人會願意因為計算問題而多付利息或減少收入,因此數值標準必須足夠精確。

按IEEE 754格式儲存的浮點數精度相當於帶有15、16或17位小數位元的十進位小數,由於存在二進位和十進位的轉換問題,具體的位元會發生變化。要獲得最高的轉換精度,必須指定17位的小數——此時可以相信前15位的精度。

你可以做個實驗,在JavaScript中輸出下面這些數值(注意不能作為字串輸出):0.1000000000000000000000000001(28位小數)、0.100000000000000000000000001(27位小數)、0.1000000000000000000000000456(28位小數)、0.09999999999999999999999(23位小數),顯示出來的結果都是數值0.1。又如,如果輸出1/3的有理數運算式,結果是0.3333333333333333。

三種格式:JavaScript的Number

實際使用中JavaScript數值還有第二種標準,它是許多電腦語言都用到的常見格式,即32位的整數,相當於C語言中的int類型。它由4個8 bit的位元組構成,可以儲存較小的整數。可能你已經在無意之中用過這種格式,但JavaScript不允許顯式聲明int,它是一種隱含的類型。

因此,JavaScript數值有三種儲存方式:字串形式的數值內容,IEEE雙精確度浮點數,或者是整數。
為什麼JavaScript要提供一種類似int的隱含數實值型別?原因有三:第一,便於執行位操作,如or(|)和移位(>>)等;二,用於數組處理;三,出於精確方面的考慮。

考慮一下數組的情況。ECMA的JavaScript標準允許每個數組有4294967295個數組元素——32 bit能儲存的最大值減1,剩下的一個用來儲存長度資料。當我們指定數組的索引時,JavaScript解譯器判斷該變數是否為一個32 bit的整數。例如,假設有一個數組var,一個變數item,如果item=2,則arr[item] = 5相當於arr[2] = 5;但是,如果索引的值看起來象是一個浮點數(或者不象數值),如item=2.0,則第三個語句相當於:arr["2.0"] = 5;。

這種表達方式很容易引起誤解,似乎JavaScript支援負數和小數形式的索引值,如arr[-6]和arr[2.35]。實際上,這類特殊的索引值會被自動轉換成字串。

再舉一個例子。假設索引變數slot1的初值等於1.1,arr[slot1]=10,現在我們讓also11 = Math.pow(Math.pow(1.1,1/20),20),從理論上看,slot1應該仍是1.1,其實不然,浮點運算的誤差將使slot1變成1.100019,如果我們再訪問arr[alot1],這時的arr[slot1]就不再是原來的arr[alot1]了。

可以看到,一涉及浮點數,事情就要複雜多了。不過你也不必太擔心,JavaScript會優先考慮整數。也就是說,當JavaScript遇到一個數值時,它會首先嘗試按照整數來處理該數值,如行得通,則把數值儲存為31 bit的整數;如果該數值不能視為整數,或超出31 bit的範圍,則把數值儲存為64位的IEEE 754浮點數。

在瀏覽器環境中,數值資料可能來自多種資料來源,包括XML、XHTML、DOM以及CSS標準等都具備向JavaScript提供資料的能力。不管資料來自何處,這些數值要麼被視為String,要麼被視為Number,如果是後則,內部儲存格式可能是整數。

四個數值:必須密切注意的臨界點

聰明的讀者一定想到了這樣一個問題:什麼時候規規矩矩的整數會突然變成捉摸不定的雙精確度浮點數?答案是:當它們的值變得非常龐大時,或者進入1和0之間時。所以說,1和0是首先必須注意的二個數值。

接下來,最大的Unicode值是1114111(7位元字,相當於/x41777777),而最大的RGB顏色值是16777215(8位元字,相當於#FFFFFF)。最大的32 bit帶正負號的整數是2147483647(10位元字),-2147483648最小,所以JavaScript內部會以整數的形式儲存所有Unicode值和RGB顏色。2147483647是第三個必須注意的數值,任何大於該值的資料將儲存為雙精確度格式。

9007199254740992(16位元字)是最大的浮點數,輸出時類似整數,所有Date對象(按毫秒計算)都小於該值,因此總是類比整數的格式輸出。它是第四個必須注意的數值。

最後,最大的雙精確度數值是1.7976931348623157e+308,超出這個範圍就要算作無窮大了。

五條原則:遠離誤差和煩惱

■ 大多數Web頁面不需要小數
避免使用小數,盡量設法使用整數。確保數組的索引都是整數。按分(而不是元)計算金額。百分比放大100倍計算以避免出現小數。儘可能不用除法(/)和模(%)運算,因為大多數情況下它們直接導致出現浮點數。如果必須使用除法,立即用Math.round方法迴歸整數運算。

■ 如果必須使用浮點數,則儘可能引入冗餘小數位——即在程式要求的運算精度之外,再增加小數位
如果程式需要5位元字的小數精度,則在運算中至少保留6位的小數,8位更好。冗餘位越多,累計誤差的影響越小。

■ 避免在同一個運算式中使用相差太大或太小的數值
對兩個非常接近的數值執行減法或比較操作很容易出錯。將很小的數值和很大數值相加無異於浪費時間,小的數值很可能被當作0。不過,很小的數值乘以很大的數值一般不會出現問題,例如2 * 12345678會得到正確的結果24691356。但是,0.1 - 0.09的結果是0.010000000000000009。

■ 用isFinite()和isNaN()檢查運算結果
通過表單提交任何數值運算結果之前,一定要先檢查資料的合法性。

■ 慎用數值運算
程式涉及的數值運算越少,引入誤差的可能就越小。視浮點數為貴客,不可任意驅使。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.