摘要:如題先陳述下問題背景偶爾測測自己寫的計算器,隨便輸入玩嘛,然后發生下面詭異的事情當我從一個輸入到十個的時候,過程顯示都是正確的,像這樣繼續輸入一個的時候,然后就這個樣子了什么原因呢看了下自己的代碼,代碼重要部分長這樣的這里用了一下強制轉化為
如題 先陳述下問題背景
偶爾測測自己寫的計算器,隨便輸入玩嘛,然后發生下面詭異的事情:
當我從一個 1 輸入到十個 1 的時候,過程顯示都是正確的,像這樣:
繼續輸入一個 1 的時候,然后就這個樣子了:
什么原因呢?
看了下自己的代碼,代碼重要部分長這樣的:
這里用了一下 parseInt 強制轉化為整數類型 (研究了之后,才懊悔,這個方法缺陷太多,研究的不深入就容易寫垃圾麻煩代碼--)
摸索問題怎么產生的出現問題,只能一點一點扒,拆分成小塊找問題。這個過程,如果有耐心的話,還是可以學到很多,提升很多,是一件很愉快的事情,工作并不是做的越多越好,而是帶著思想做的越深越好。
廢話結束
首先嘗試一下 console parseInt
嘗試剛剛數字, 從十個 1 上開始加
著實搞不懂這是怎么回事唉
parseInt 深入理解 (字符串,小數,整數之間的轉換)除了我試出來的這個問題,網上還看到這樣的奇葩 ParseInt(0.0000008, 10)
所以了解一下 parseInt 究竟在轉換過程中做了啥是非常有必要的
parseInt 在將字符串或者小數(我們眼中的而已,其實他都一視同仁)轉換為整數時,做了那幾步呢?
取出參數
將傳入的第一個參數轉為字符串 調用了String()方法
根據第二個參數,再對字符串進行int轉換
String()方法會讓原來的參數改變,比如無數個1
再比如鏈接中提到的 0.0000008
可見String()將其科學計數法再轉為字符串,造成只取到了 8
通俗理解一下,第一個參數是字符串還好,如果不是字符串就造成麻煩了,非字符串的數值在調用 String 進行轉換時會出問題的,這就是 parseInt 弊端
(感興趣的 可以再深入 String 這個方法做了哪些事情吧。這里根據es5標準寫了一篇String 內部處理邏輯標準)
繼續嘗試 parseInt ,百試不厭,就會發現,當增加到一定大的數時候,會發現不變了,臨界值如下:
不僅僅嘗試了 parseInt 同時嘗試了 + 單元運算符,結果一樣, 這應該跟 paseInt 沒多大關系了吧,應該是因為編碼存儲之類的問題吧
所以啊,即使沒有最大值問題, parseInt 還是少用,可以Math.round等就不會出現這個問題,為啥呢,可以深入一波
是怎么聯系上 encoded 問題的呢?上面例子測試,發現,當 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,為什么呢?
為什么是 2^53 而不是 2^52?根據 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呢?
為什么是 2^53 而不是 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 標準JS 浮點運算丟失精度問題
在計算 0.1 + 0.1 出錯的問題上,精度是怎么丟失的呢,這個問題和 parseInt 思考方式基本類似,看這個過程中有哪些步驟,在哪步會丟失精度
首先這是一個表達式,要先將表達式的 左右對象 裝進計算機
轉換為二進制
用二進制科學計數法表示
表示成 IEEE 754 形式
第一步和第三步都有可能丟失精度
回到最開始的問題,怎么解決呢?替換 parseInt 為 Math.round
若想用 大于 2^53 的數進行計算的話,思考下導入什么 bitInterger 的庫用一用吧
多看幾個例子:
以上屬于精度丟失問題,間隔計算
科學計數法存儲展示問題
參考
http://2ality.com/2012/04/num...
https://en.wikipedia.org/wiki...
http://es5.github.io/#x8
http://blog.csdn.net/JustJava...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92951.html
摘要:基于這個問題運動基礎問題,我想應該也有一部分人沒有認真對待過中浮點數的四則運算出現的問題。解決方案引自解決方案為了解決浮點數運算不準確的問題,在運算前我們把參加運算的數先升級的的次方到整數,等運算完后再降級的的次方。 基于這個問題:javascript運動基礎問題 ,我想應該也有一部分人沒有認真對待過js中浮點數的四則運算出現的問題。 1.問題描述 示例代碼: var x ...
摘要:意外四舍五入會損害程序的可靠性和安全性。下面是一些例子構造函數與其他基本類型一樣,可以使用構造函數創建。總結是一種新的數據類型,用于當整數值大于數據類型支持的范圍時。 為了保證的可讀性,本文采用意譯而非直譯。 想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等著你! BigInt數據類型的目的是比Number數據類型支持的范圍更大的整數值。在對大整數執行數學運算時,以任意精...
摘要:和深入理解在和深入理解這篇博客里筆者曾做過總結,我們知道試單線程的產物,兩個函數就是利用了插入代碼的方式實現了偽異步,和的原理實際上是一樣的。綜上所述,其實終歸是單線程產物。無論如何異步都不可能突破單線程這個障礙。 說明:??這是筆者平時積累的一些覺得比較有意思或是比較有難度的JavaScript題目理解和心得,會保持長期更新。 1.setTimeout和setInterval深入理解...
摘要:如圖意義位用來表示符號位位用來表示指數位表示尾數浮點數,比如無限循環無限循環此時只能模仿十進制進行四舍五入了,但是二進制只有和兩個,于是變為舍入。這即是計算機中部分浮點數運算時出現誤差,丟失精度的根本原因。 showImg(http://ww1.sinaimg.cn/large/9c47d583gy1fmtw1ma9g4j21hc0u0ach.jpg); 前言 最近一直有小伙伴跟我說J...
摘要:本文通過介紹的二進制存儲標準來理解浮點數運算精度問題,和理解對象的等屬性值是如何取值的,最后介紹了一些常用的浮點數精度運算解決方案。浮點數精度運算解決方案關于浮點數運算精度丟失的問題,不同場景可以有不同的解決方案。 本文由云+社區發表 相信大家在平常的 JavaScript 開發中,都有遇到過浮點數運算精度誤差的問題,比如 console.log(0.1+0.2===0.3)// fa...
閱讀 999·2021-11-15 18:06
閱讀 2367·2021-10-08 10:04
閱讀 2652·2019-08-28 18:03
閱讀 897·2019-08-26 13:42
閱讀 1920·2019-08-26 11:31
閱讀 2423·2019-08-23 17:13
閱讀 926·2019-08-23 16:45
閱讀 2054·2019-08-23 14:11