偶爾測測本身寫的計算器,隨便輸入玩嘛,而後發生下面詭異的事情:
當我從一個 1 輸入到十個 1 的時候,過程顯示都是正確的,像這樣:html
繼續輸入一個 1 的時候,而後就這個樣子了:git
什麼緣由呢?
看了下本身的代碼,代碼重要部分長這樣的:github
這裏用了一下 parseInt 強制轉化爲整數類型 (研究了以後,才懊悔,這個方法缺陷太多,研究的不深刻就容易寫垃圾麻煩代碼--)segmentfault
出現問題,只能一點一點扒,拆分紅小塊找問題。這個過程,若是有耐心的話,仍是能夠學到不少,提高不少,是一件很愉快的事情,工做並非作的越多越好,而是帶着思想作的越深越好。
廢話結束瀏覽器
首先嚐試一下 console parseInt
嘗試剛剛數字, 從十個 1 上開始加ide
着實搞不懂這是怎麼回事唉測試
除了我試出來的這個問題,網上還看到這樣的奇葩 ParseInt(0.0000008, 10)編碼
因此瞭解一下 parseInt 究竟在轉換過程當中作了啥是很是有必要的es5
parseInt 在將字符串或者小數(咱們眼中的而已,其實他都一視同仁)轉換爲整數時,作了那幾步呢?spa
String()方法會讓原來的參數改變,好比無數個1
再好比連接中提到的 0.0000008
可見String()將其科學計數法再轉爲字符串,形成只取到了 8
通俗理解一下,第一個參數是字符串還好,若是不是字符串就形成麻煩了,非字符串的數值在調用 String 進行轉換時會出問題的,這就是 parseInt 弊端
(感興趣的 能夠再深刻 String 這個方法作了哪些事情吧。這裏根據es5標準寫了一篇String 內部處理邏輯標準)
繼續嘗試 parseInt ,百試不厭,就會發現,當增長到必定大的數時候,會發現不變了,臨界值以下:
不只僅嘗試了 parseInt 同時嘗試了 + 單元運算符,結果同樣, 這應該跟 paseInt 沒多大關係了吧,應該是由於編碼存儲之類的問題吧
因此啊,即便沒有最大值問題, parseInt 仍是少用,能夠Math.round等就不會出現這個問題,爲啥呢,能夠深刻一波
上面例子測試,發現,當 parseInt 的第一個參數傳入的是數字而且愈來愈大時,值就停留在 9007199254740992。固然排除特殊的科學計數法(這會致使 js 在表示 number 時隱藏部分數字)後變換隻獲得1或者8或者其餘等等狀況,爲何停留在這個數字呢?
查看 ES5 標準, 發現 JS 對 number encoded 的處理很獨特,總結以下:
Number類型統一按浮點數處理,64位存儲,整數是按最大53位來算最大最小數的Number value
primitive value corresponding to a double-precision 64-bit binary format IEEE 754 value.
查閱 IEEE 754
圖片顯示雙精度 64 位浮點數的存儲格式爲:
s * m * 2^e s 是符號位,表示正負,由 1 bit m 是小數位,由 52 bits e 是指數位, 由 11 bits
64位表示雙精度浮點數,能夠表示 2^64 - 2^53 + 3 種數值
這些數中包括 number 所包括的各類類型(NaN , infinity, 浮點數),這些數值是怎麼在 64bits 中存儲的呢?能夠看這一篇JS雙精度64位 Number
其中正常的浮點數又包含了不丟失精度的和可能會丟失精度的,不丟失精度的數通俗理解就是加 1 不會算錯,因此 Number.MAX_SAFE_INTEGER 的值爲 9007199254740991 由於 9007199254740991 + 1 不會算錯,若是用 9007199254740992 +1 就會算錯了,以下圖
因此稱之爲 max_safe_interger
其實 number 能表示的最大整數是 2^53,爲何呢?
根據 IEEE754 制定的標準,應該只有 52 bits 用來表示小數位的,最大也應該是 2^51 - 1 呀!
先普及 52 bits 表示的二進制轉換爲十進制爲何最大是 2^51 - 1,有助於小白理解 2^53 而不至於混淆。
52 bits 當 52 位上都是 1 的時候,轉換爲十進制獲得的數最大 根據二進制轉十進制的方法,將 52 個 1……1 轉換爲十進制,方程爲: 2^0 + 2^1 + 2^2 + …… + 2^51 觀察得知這是一個 首位爲 2 公比爲 2 等比數列求合 a1 * (1 -q^n)/1-q (q != 1) na1 (q == 1) 因此 52 bits 能表示的最大十進制數爲 2^51 - 1,同理 53 bits 能表示的最大十進制數爲 2^52 - 1
如今思考爲何是 2^53 呢?
Why 53 bits? You have 53 bits available for the magnitude (excluding the sign), but the fraction comprises only 52 bits. How is that possible? As you have seen above, the exponent provides the 53rd bit: It shifts the fraction, so that all 53 bit numbers except the zero can be represented and it has a special value to represent the zero (in conjunction with a fraction of 0).
這段話的意思就是,在表示最大數的時候,存儲指數位的 11 bits 分給小數位 1 bit,就變成了 53 bits,就算這樣也應該最大是 2^53 - 1 啊,爲何不是 2^53 - 1呢?
Why is the highest integer not 2^53−1? Normally, x bit mean that the lowest number is 0 and the highest number is 2x−1. For example, the highest 8 bit number is 255. In JavaScript, the highest fraction is indeed used for the number 2^53−1, but 2^53 can be represented, thanks to the help of the exponent – it is simply a fraction f = 0 and an exponent p = 53
這段話意思是說,最高的小數位對於 2^53 - 1是有必要的並且已經有的,可是 2^53 也是能夠轉化過來的。什麼意思呢,對於二進制來講,小數點前保留一位,規格化後始終是 1.*,節省了 1 bit,這個 1 並不須要保存。
發現超過 2^53 的十進制數也是能夠表示的,那麼是怎麼存儲的的,仍是指數位提供了幫助,這也就是爲何能夠在 2^53 上以 2 的倍數
變化,能夠加 2,加 4 ……,可是加 1 不行,不能精確顯示。一樣比 2^53 大且比 2^54 小的數在瀏覽器中顯示,是其 2 的倍數才能正確顯示,不然不能
,x can be represented in the range 2^53 ≤ x < 2^54. In row (p=54), that spacing increases to multiples of four, in the range 2^54 ≤ x < 2^55 …… and so on
因此 max_safe_interger 是 2^53 - 1
以前常見到 js unicode utf-16 引發的一些問題,此次碰見的是 js encoded 問題 js string 存儲是 utf-16 encoding form js number 存儲是 IEEE 754 雙精度浮點數 64 bits 標準
在計算 0.1 + 0.1
出錯的問題上,精度是怎麼丟失的呢,這個問題和 parseInt
思考方式基本相似,看這個過程當中有哪些步驟,在哪步會丟失精度
首先這是一個表達式,要先將表達式的 左右對象 裝進計算機
第一步和第三步都有可能丟失精度
多看幾個例子:
以上屬於精度丟失問題,間隔計算
科學計數法存儲展現問題
參考
http://2ality.com/2012/04/num...
https://en.wikipedia.org/wiki...
http://es5.github.io/#x8
http://blog.csdn.net/JustJava...