第一次a 0.1 + 0.2 === 0.3 bug 在製作中咬了我,我做了大多數開發人員都會做的事情:我搜尋、找到並引用;浮點很奇怪,&引用;並繼續前進。多年後我才真正查看這些位並理解原因。一旦我這樣做了,一整類數字錯誤就不再神秘了。本指南將介紹 IEEE 754 轉換器向您展示的內容 IEEE 754 轉換器 在 toolz。dev 上,為什麼盯著符號、指數和尾數是建立關於電腦如何儲存小數的真實直覺的最快方法。
TL;DR: 電腦依照 IEEE 754 標準將浮點數儲存為符號、二的冪和小數有效數。 IEEE 754 轉換器將像 3.14159 這樣的小數編碼到這些精確位元中,並將位元解碼回一個值。這 IEEE 754 轉換器 兩者都可以完全在瀏覽器中實現單精度和雙精度,並分解符號、指數和尾數。
什麼是 IEEE 754?
IEEE 754 是該標準,於 1985 年首次發布並修訂為 IEEE 754-2019,這定義了電腦如何以二進位表示浮點數。幾乎每個 CPU、GPU 和程式語言都遵循它,這就是為什麼 a double 在 C、Java、Python 和 JavaScript 中表現相同。當人們說"浮點,"它們幾乎總是指 IEEE 754 二進位浮點。
核心思想是數字被儲存在三個部分。有一個表示正或負的符號位。有一個指數,該值按 2 的冪縮放。並且有一個尾數,也稱為分數或有效數,它保存精確的數字。加在一起,法數是符號乘以 1。尾數乘以 2 的指數。它是以二為基數的科學符號,打包成固定位數。
您每天遇到的兩種格式是單精度(稱為二進位 32)和雙精度(稱為二進位 64)。 Single 使用 1 個符號位元、8 個指數位元和 23 個尾數位,總共 32 位元。 Double 使用 1 個符號位元、11 個指數位元和 52 個尾數位,總共 64 位元。 Double 是大多數語言中的預設浮點類型;單一在 GPU 和記憶體敏感程式碼中很常見。
IEEE 754 轉換器實際上有什麼作用?
IEEE 754 轉換器會取得一個數字並向您顯示其精確的位元模式,分為這三個字段,以及十六進位形式和從位元讀回的值。它有兩種工作方式:輸入十進位對其進行編碼,或貼上二進位或十六進位位元模式以將其解碼回其表示的數字。
在 toolz。dev 上,工作流程很短。選擇單精度或雙精度。選擇輸入是十進制數、十六進位位模式還是二進位位模式。輸入值。該工具顯示符號位、指數欄位、尾數、完整位元、十六進位、有偏和無偏指數以及精確的儲存值。每個欄位都有一個複製按鈕。
之所以要查看所有這些,而不是相信您的語言列印的數字,是因為列印的數字是遺漏的。當您的控制台顯示時 0.1,它向您展示了一個圓潤、友好的渲染。這些位元告訴您真正儲存的內容,而這兩者之間的差異在於浮點錯誤所在的位置。
為什麼0.1加0.2不等於0.3?
這是典型的例子,轉換器使其具體化。值 0.1 無法精確地用二進位浮點表示,出於同樣的原因,1/3 無法精確地寫成十進位。在以十為基數的情況下,1/3 是 0.3333。。永遠。在以二為基數時,0.1 是一個永遠不會終止的重複分數,因此必須將其四捨五入以適合 52 個尾數位元。
解碼 0.1 的雙倍,位元實際保存的值為 0.10000000000000000055511115123125782702118153404541015625。 0.2 的雙倍同樣是頭髮太大。將兩個四捨五入的值相加,結果略大於 0.3,但 0.3 的雙倍值稍小,因此兩者不相等。沒有任何損壞。每一步都完全按照 IEEE 754 的要求進行,轉換器可讓您觀看它的發生,而不是憑信心進行。
一旦你看到了這一點,修復自然就會發生:永遠不要比較浮點數以獲得完全相等。在小公差範圍內進行比較,或使用整數美分作為貨幣,或在需要精確的十進制算術時使用十進制庫。該錯誤從未出現在加法中。它期望二進制格式能夠精確地儲存小數。
符號、指數和尾數是什麼?
每個欄位都有一個特定的作業,轉換器對所有三個欄位進行標記。
符號位是最簡單的:0 表示正,1 表示負。由於符號是單獨的位,IEEE 754 具有正零和負零,它們在比較中表現相同,但攜帶不同的位。
指數欄位儲存 2 的冪,但添加了偏差,因此它可以表示負指數,而無需單獨的符號。單精度偏差為 127,雙精度偏差為 1023。因此,單精度儲存的指數域為 127 表示實際指數為零。 toolz。dev 轉換器顯示兩個數字:存在於位元中的偏置值,以及它實際應用的 2 的無偏置冪。這種區別讓很多人感到困惑,這就是為什麼該工具會詳細說明它,而不是讓你在腦海中減去它。
尾數包含尾數的小數部分。對於正常數字,有一個未儲存的隱含前導 1,因為歸一化二進位有效數總是以 1 開頭,因此格式透過將其省略而獲得免費的精度位。這就是為什麼單精度給出大約 7 位小數,雙精度給出大約 15 到 16,即使尾數欄位是 23 和 52 位元。
以下是兩種格式如何逐場排列:
| 財產 | 單(二進制32) | 雙(二進制64) |
|---|---|---|
| 總位數 | 32 | 64 |
| 簽名位 | 1 | 1 |
| 指數位 | 8 | 11 |
| 尾數 | 23 | 52 |
| 指數偏差 | 127 | 1023 |
| 大約。十進制數字 | 7 | 15 至 16 |
| 通用名 | 漂浮 | 雙 |
如何將位元轉換回十進制數?
解碼與編碼一樣有用,它是從記憶體轉儲、二進位檔案格式或儲存原始浮點的網路協定中讀取值的方式。將輸入類型設定為十六進位或二進位,並貼上精確度預期的精確位元模式:8 個十六進位數字或 32 位元(對於單一)、16 個十六進位數字或 64 位元(對於雙)。
例如,單精度六角形 40490FDB 解碼為 3.1415927,最接近 pi 的浮點數。轉換器也接受可選 0x 前綴並忽略空格和下劃線,因此您可以在找到分組位元組時貼上它們。如果您給它錯誤的位數,它會準確地告訴您需要多少精度,而不是默默地截斷,這是我希望需要更多工具來編寫的錯誤訊息。
此解碼方向自然與一般方向配對 數目 基數轉換器 當您需要在不進行浮點解釋的情況下在十進制、十六進制、二進制和八進制之間移動值時,並且 二進制翻譯 當你使用文字而不是數字時。我經常接觸到這三個人,以至於他們住在我書籤的同一角。
無窮大、NaN 和零是如何表示的?
特殊值是 IEEE 754 變得聰明的地方,轉換器可以為您識別每個情況,這樣您就不必記住模式。
指數域充當開關。當每個指數位元都是 1 時,您處於特殊範圍內:全零尾數表示無窮大,任何非零尾數表示 NaN,而不是數字。正無窮大和負無窮大僅因符號位而異。 NaN 是從零除以零或負的平方根等運算中得到的,它具有不等於自身的著名屬性。
當每個指數位元為 0 時,您處於另一個特殊範圍內:全零尾數為零,正或負取決於符號位,非零尾數是次正規數。次正規數填補了零和最小法線數之間的空白,用精度換取表示非常小幅度的能力。它們使用 1 的有效指數減去偏差,然後刪除隱含的前導 1,當轉換器顯示無偏指數時,它們會考慮該前導 1。
其他一切,其中指數域既不是全部零也不是全部一,都是普通數。當計算產生您意想不到的值時,能夠查看位模式並立即知道您正在查看的這五種情況中的哪一種是真正的調試超級大國。
我什麼時候真正需要這個?
你需要的次數比你知道看的時候想像的還要多。當數字結果因舍入錯誤而偏離時,我會伸手去拿它,並且我想確認該值是否完全可表示。我在編寫或調試二進位序列化器、檔案格式或儲存浮點的有線協定時使用它,這樣我就可以驗證位元組與我的意圖相符。在比較單精度和雙精度時,例如決定 GPU 著色器是否可以使用,這是必不可少的 float 不失太多準確性。它是我所知道的電腦架構最清晰的教具之一,因為看到這些領域使抽象標準變得有形。
如果你像我一樣跨堆疊構建,浮點就會出現在令人驚訝的地方:Laravel API 中的價格計算、React 中的畫布動畫、破壞座標的 CSV 匯出。了解儲存格式是一項小投資,可以在所有這些方面獲得回報。我寫過這樣的工具如何融入更廣泛的套件中 web 開發人員工具包,並且 數基轉換器指南 和 二進位翻譯器指南 更深入地覆蓋鄰近的低階轉換。
浮點精度的限制是什麼?
一旦你可以看到這些位,格式的限制就不再是抽象的了。雙倍有 52 個儲存的尾數位元加上隱含的前導 1,結果約為 15 到 16 個有效十進制數字。推過它,精度就會悄悄消失。雙倍可以表示的最大整數是 2 的 53 次方,即 9007199254740992。加一個,結果向下舍入,因為下一個可表示值是兩個,而不是一個。這就是為什麼對所有數字使用雙倍的語言(其中包括 JavaScript)會暴露 a Number.MAX_SAFE_INTEGER 始終保持在該值,以及為什麼以 JSON 編號形式發送的大型資料庫 ID 會悄悄損壞。
一個可表示值與下一個可表示值之間的差距在最後稱為單位,或 ULP,並且隨著大小的增長而增長。接近 1.0 時,差距很小;接近十億就是幾百;接近最大兩倍,大約 1.8 乘以 10 到 308,鄰居之間的差距在天文上很大。轉換器使這一點可見:對附近的兩個大數字進行編碼,您經常會發現它們共享相同的位,因為它們之間根本沒有表示。了解 ULP 會阻止您期望比格式提供的分辨率更高的分辨率。
一個相關的陷阱是災難性的取消。當你減去兩個幾乎相等的浮點數時,前導數字就會抵消,你就會留下低位,這是一開始最不準確的部分。即使每個輸入看起來都很精確,結果也可能以捨入誤差為主。這就是為什麼在數值上仔細地對程式碼重新排序操作以避免減去接近的數量,以及為什麼在樸素循環中對一長串浮點數求和會累積卡漢求和等更好演算法避免的錯誤。
實際要點是一致的。不要將錢儲存為浮動;使用整數美分或十進制類型,因為像 0.10 這樣的值不能完全表示,並且錯誤會在數千筆交易中複合。不要以完全相等的方式比較浮動;在適合您問題的容差範圍內進行比較。當一個非常大的整數必須在往返中倖存下來時,將其保留為字串或使用任意精度的整數類型,而不是相信雙倍來保存它。一旦轉換器向您展示了格式為何表現如此,這些規則中的每一條都更容易記住。
常見問題
如何將十進制數轉換為 IEEE 754?
選擇單精度或雙精度,保持輸入類型為十進制,輸入您的數字,然後轉換。該工具將該值編碼為其 IEEE 754 位元模式,並顯示符號位元、指數欄位、尾數、全二進位和十六進位。所有這些都在您的瀏覽器中運行。
單精度和雙精度有什麼區別?
單精度 (binary32) 使用 32 位元:1 個符號、8 個指數和 23 個尾數,給出大約 7 個十進制數字的精度。雙精度 (binary64) 使用 64 位元:1 個符號、11 個指數和 52 個尾數,給出大約 15 到 16 位數字。 Double 是大多數語言中的預設浮動類型; single 在 GPU 和記憶體受限程式碼中很常見。
為什麼 0.1 不轉換為精確值?
0.1 在二進位浮點中沒有有限的表示,就像 1/3 沒有有限的十進位一樣。最接近的可表示雙倍是0.1000000000000000000555111151231257827021181583404541015625。轉換器顯示從位元讀回的儲存值,因此您可以看到導致舍入意外(例如 0.1 + 0.2 不等於 0.3)的微小差異。
符號、指數和尾數是什麼?
正號位為 0,負號位為 1。指數欄位儲存 2 的冪並添加偏差(單 127,雙 1023),這就是為什麼該工具同時顯示位元中的偏差值及其應用的無偏冪。尾數包含有效數的小數部分,對於正常數字,隱含的前導 1。
我可以將 IEEE 754 位元轉換回十進制數嗎?
是的。將輸入類型設定為十六進位或二進位並貼上精確的位元模式:8 個十六進位數字或 32 位元用於單精度,16 個十六進位數字或 64 位元用於雙精度。該工具對其進行解碼並顯示它表示的十進位值以及完整的欄位細分。
無窮大、NaN 和零是如何表示的?
當每個指數位元為 1 時,全零尾數表示無窮大,非零尾數表示 NaN。當每個指數位元為 0 時,全零尾數為零(正或負取決於符號位元),非零尾數是次正規數。轉換器會為您標記每種情況。
對於敏感號碼使用此工具安全嗎?
是的。轉換完全在瀏覽器中以 JavaScript 運行。沒有網路請求攜帶您的輸入,不會儲存任何內容,並且頁面載入後工具會離線工作,因此您輸入的任何值都會保留在您的裝置上。
轉換器期望什麼十六進位格式?
普通十六進位數字,帶有可選的 0x 前綴和用於分組的空格或下劃線,這些數字將被忽略。單精度期望 8 個十六進位數字,雙精度期望 16。例如,40490FDB 是單精度 pi (3.1415927)。
自己用免費探索這些片段 IEEE 754 轉換器. 它對單精度和雙精度浮點進行編碼和解碼,完全在您的瀏覽器中,沒有上傳任何內容。



