valid floating point value什么意思_为什么 0.1 + 0.2 = 0.300000004?
1、《往期精選優秀博文都在這里了!》2、求求你!數據庫不要再使用外鍵了?3、還在寫慢SQL?4、ELK太笨重了?想放棄?快試試日志系統新貴Loki吧!5、誰再悄咪咪的吃掉異常,我上去就是一 JIO
JavaScript 作為一門誕生自上個世紀 90 年代的編程語言,從誕生之初就因為詭異的隱式類型轉換等原因被黑,很多 JavaScript 的開發者還會吐槽浮點數加法的『奇葩』問題 — 為什么 0.1 + 0.2 在 JavaScript 中不等于 0.3,相信很多人都對這個問題的答案有一個大概的認識,但是都沒有深入研究過,這個問題的答案讓 William Kahan 在 1989 年獲得圖靈獎。
> 0.1 + 0.20.30000000000000004其實有上述問題的不止 JavaScript 一門編程語言,幾乎所有現代的編程語言都會遇到上述問題,包括 Java、Ruby、Python、Swift 和 Go 等等,你可以在 https://0.30000000000000004.com/ 中找到常見的編程語言在計算上述表達式的結果。這不是因為它們在計算時出現了錯誤,而是因為浮點數計算標準的要求。
floating-point-math圖 1 - 常見的浮點數『錯誤』
從最開始接觸 C 語言編程,作者就接觸到了浮點數 float,然而在很長一段時間中,作者都將編程中的浮點數和數學中的小數看做同一個東西,不過當我們重新審視它們時,會發現這兩個概念的不同之處。
- 編程中的浮點數的精度往往都是有限的,單精度的浮點數使用 32 位表示,而雙精度的浮點數使用 64 位表示;
 - 數學中的小數系統可以通過引入無限序列 ... 可以任意的實數;
 
在數學上我們總有辦法通過額外的符號表示更復雜的數字,但是從工程的角度來看,表示無限精度的數字是不經濟的,我們期望通過更小和更快的系統表示范圍更大和精度更高的實數。浮點數系統是在工程上面做的權衡,IEEE 754 就是在 1985 年建立的浮點數計算標準,它定義了浮點數的算術格式、交換格式、舍入規則、操作和異常處理。討論浮點數也無法脫離該標準,為了回答今天的問題,我們將從以下的兩個角度觸發:
- 二進制無法在有限的長度中精確地表示十進制中 0.1 和 0.2;
 - 單精度浮點數、雙精度浮點數的位數決定了它們能夠表示的精度上限;
 
二進制與十進制
我們日常生活中使用的數字基本都是 10 進制的,然而計算機使用二進制的 0 和 1 表示整數和小數,所有有限的十進制整數都可以無損的轉換成有限長度的二進制數字,但是要在二進制的計算機中表示十進制的小數相對就很麻煩了,我們以 0.375 為例介紹它在二進制下的表示:
小數點后面的位數依次表示十進制中的 0.5、0.25、0.125 和 0.0625 等等,這個表示方法非常好理解,每一位都是前一位的一半。0.375 在二進制表示看來確實是『整數』。然而如下圖所示,想要使用二進制表示十進制中的 0.1 和 0.2 是比較復雜的:
decimals-binary-representation圖 2 - 二進制表示的十進制小數
無論是 0.1 還是 0.2,這兩個數字都不是二進制中的『整數』,我們沒有辦法精確地表示它們,只能通過無限循環小數嘗試接近它們的真實值;與之相似的是,它們相加的結果 0.3 也無法用有限長度的二進制表示:
dot-three-binary-representation圖 3 - 二進制表示的 0.3
這三個不同的數字都會在最后的小數部分無限循環 1100 來趨近于真實值,如果計算機中的浮點數可以表示無限循環小數就有可能解決這個問題,但是事實的真相是浮點數只會表示有限小數,所有超過特定精度的數字都會做舍入處理。
精度上限
編程語言中的浮點數一般都是 32 位的單精度浮點數 float 和 64 位的雙精度浮點數 double,部分語言會使用 float32 或者 float64 區分這兩種不同精度的浮點數。想要使用有限的位數表示全部的實數是不可能的,不用說無限長度的小數和無理數,因為長度的限制,有限小數在浮點數中都無法精確的表示。
float-and-double圖 4 - 單精度與雙精度浮點數
- 單精度浮點數 float 總共包含 32 位,其中 1 位表示符號、8 位表示指數,最后 23 位表示小數;
 - 雙精度浮點數 double 總共包含 64 位,其中 1 位表示符號,11 位表示指數,最后 52 位表示小數;
 
我們以單精度浮點數 0.15625 為例,介紹該浮點數在計算機二進制中的表示方法,如下圖所示,符號位 0 表示該浮點數為正數,中間的 8 位指數總共可以表示 256 個數字,其中從 [0, 126] 表示 [-127, -1],而 [127, 255] 表示 [0, 128],二進制的 01111100 是十進制的 124,表示 ,最后的 23 位是二進制的小數 0.25:
floating-number-example圖 5 - 0.15625 的單精度浮點數表示
通過上圖中的公式 可以將浮點數的二進制表示轉換成十進制的小數。0.15625 雖然還可以用單精度的浮點數精確表示,但是 0.1 和 0.2 只能使用浮點數表示近似的值:
dot-one-dot-two-floating-number圖 6 - 0.1 和 0.2 的單精度浮點數表示
因為 0.2 和 0.1 只是指數稍有不同,所以上圖中只展示了 0.1 對應的單精度浮點數,從上圖的結果我們可以看出,0.1 和 0.2 在浮點數中只能用近似值來代替,精度十分有限,因為單精度浮點數的小數位為 23,雙精度的小數位為 52,同時都隱式地包含首位的 1,所以它們的精度在十進制中分別是 和 位。
因為 0.1 和 0.2 使用單精度浮點數表示的實際值為 0.100000001490116119384765625 和 0.20000000298023223876953125[^7],所以它們在相加后就得到的結果與我們在一開始看到的非常相似:
dot-three-floating-number圖 7 - 0.1 加 0.2 的結果
上圖只是使用單精度浮點數表示的數字,如果使用雙精度浮點數,最終結果中的 3 和 4 之間會有更多的 0,但是小數出現的順序是非常相似的。浮點數的運算法則相對來說比較復雜,感興趣的讀者可以自行搜索相關的資料,我們在這里不展開介紹了。
總結
當我們在不同編程語言中看到 0.300000004 或者 0.30000000000000004 時不應該感到驚訝,這其實說明編程語言正確實現了 IEEE 754 標準中描述的浮點數系統,在使用單精度和雙精度浮點數時也應該牢記它們只有 7 位和 15 位的有效位數。
在交易系統或者科學計算的場景中,如果需要更高的精度小數,可以使用具有 28 個有效位數的 decimal 或者直接使用分數,不過這些表示方法的開銷也隨著有效位數的增加而提高,我們應該按照需要選擇最合適的方法。重新回到今天的問題 — 0.1 和 0.2 相加不等于 0.3 的原因包括以下兩個:
- 使用二進制表達十進制的小數時,某些數字無法被有限位的二進制小數表示;
 - 單精度和雙精度的浮點數只包括 7 位或者 15 位的有效小數位,存儲需要無限位表示的小數時只能存儲近似值;
 
浮點數系統的設計是一個比較有趣的工程問題,因為操作系統一般都是 32 位或者 64 位的,浮點數充分利用了 32/64 位的比特,將每一位的作用都發揮到極致,使用最緊湊和簡潔的方式實現了盡可能高的精度。到最后,我們還是來看一些比較開放的相關問題,有興趣的讀者可以仔細思考一下下面的問題:
- 有哪些編程語言內置了高精度的浮點數或者小數?
 - 如何實現一個可以精確表示所有實數(包括有理數和無理數)的系統?
 
圖是怎么畫的
本文使用的是Sketch
往期熱門文章:
1、《歷史文章分類導讀列表!精選優秀博文都在這里了!》
2、圖解Spring循環依賴,看過之后面試再也不用慌了!
3、他來了!IDEA 2020.1 新版介紹!不過升級前請注意避坑!
4、七個略火的Spring Boot+Vue開源項目!
5、Linux 11個炫酷的終端命令!你知道幾個?
6、十個你可能不曾用過的Linux命令!巨好用!
7、分庫分表? PK NewSQL數據庫!
8、快給你的Spring Boot做個埋點監控吧!
9、一入職!就遇到上億(MySQL)大表的優化....
10、驚呆了,Spring Boot居然這么耗內存!
總結
以上是生活随笔為你收集整理的valid floating point value什么意思_为什么 0.1 + 0.2 = 0.300000004?的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: python合并csv文件_Python
 - 下一篇: 武侯区房屋租赁备案地址在哪里?(武侯区房