![]()
???????????????????????????????????????????????????????????????????????
眾所不周知,大部分編程語言都算不出0.1+0.2=0.3。
比如Javascript一通計算下的結果就為:0.30000000000000004!
![]()
cloud.tencent
你可能會說:“你先等會兒!”
然后打開windows上自帶的計算器,輸入“0.1+0.2”,然后拿著計算結果掄圓了準備打我的小臉。
![]()
自制
其實,計算器看似正確的結果,只是計算器把存在精度問題的最后幾位在顯示的時候舍入掉了而已。我們可以理解為這是一種“障眼法”。
![]()
自制
那問題到底出在哪兒?
要理解這個現象,得先搞明白計算機是怎么看待小數的。
我們平時用的是十進制,但計算機只懂二進制,也就是那一堆0和1。把十進制小數轉成二進制,用的是“乘2取整”法。以 0.1 為例:
![]()
自制
看出來了嗎?0.1 的二進制是無限循環的:0.00011001100110011……(0011 循環)。
同樣,0.2 的二進制也是無限循環:0.0011001100110011……(0011 循環)。
看不出來也不要緊,我們只要記住:我們十進制世界里大部分稀松平常的小數,二進制都是無法精準表達的。
0.1 和 0.2在二進制世界里,就是“無限循環小數”,也就如同十進制無法精確表示 1/3 一樣。
除了0.1和0.2以外,π、√2等數學上的無理數(無限不循環小數),二進制下更是超綱了,計算機同樣也是無法精確表示的。
弄懂了這個概念,我們再來看計算機的存儲法則。
我們現在主流的編程語言,通用的存儲法基本都是“IEEE 754浮點數標準”,只要是使用了這個存儲標準的語言,比如JavaScript、C/C++、Java、以及曾號稱人人都應該掌握的Python等等,都是無法準確計算出“0.1+0.2=0.3”的。
![]()
自制
IEEE 754是由電氣與電子工程師協會(IEEE)于1985年制定的浮點運算技術標準。
在這部“數字憲法”制定之前,各大計算機廠家都有自己的浮點數存儲法則,你不兼容我的,我不care你的,主打一個各論各的,造成了很多混亂。
IEEE 754標準就規定了浮點數在計算機中的存儲格式,常見的有32位單精度和64位雙精度。每個數被拆分為符號位、指數位和尾數位三部分,尾數位長度是固定的(如雙精度為52位),那么遇到無限循環或者無限不循環小數的時候,怎么辦?那就是把尾數“一剪沒”了。
這樣截斷或舍入,就給0.1和0.2造成了存儲誤差。兩個近似值相加,誤差累積,就導致了0.1+0.2≠0.3的現象。
![]()
梅開二度、二度返場|《夏洛特煩惱》
你可能又得問:浮點數存儲為啥非得把尾數截了?計算機這么牛,全存了不行嗎?
答案很簡單:不是不想,是做不到。
很多人以為計算機存儲和計算都是絕對精確的,其實這是個誤解。
存整數確實可以——比如數字 42,轉成二進制 101010,放進固定大小的空間里,只要空間夠大(不溢出),拿出來還是 42,分毫不差。就像把雞蛋放進倉庫,拿出來還是那個雞蛋。
但問題是,數學世界里的數遠不止如此。無限循環和無限不循環的數,遠遠多于整數。
計算機的存儲空間是有限的,無論用多少位,都裝不下一個無限長的二進制串。所以,它只能截取前幾十位,后面的直接扔掉。
你可能又會想:那能不能把空間做得再大點,存更多位?從幾十位變成幾百位也行啊!
理論上可以,但實際意義不大。
對絕大多數工程、圖形、科學計算來說,“IEEE 754浮點數存儲法”所用的 64 位雙精度(約 15-17 位有效數字)已經足夠精確。
![]()
對不同領域來說,計算精度要求是不同的,天文學家能算準光年就很牛了|網絡
如設計飛機、渲染電影、模擬天氣,沒人需要知道 π 的第10000位是什么(當然,π能不能算到最后一位,在數學上意義重大,可能意味著數學大廈的地基比如微積分、極限理論被動搖)。況且,無理數有無窮多個,再大的空間也裝不完。
所以計算機選擇了一種“務實”的策略:用有限的存儲,近似地表示所有的實數。專業的相關學科就叫做計算機數值分析,研究的就是“近似”的藝術。
對于任何一個數值計算問題,數值分析都會追問:誤差有多大?算法快不快?結果穩不穩?能不能算出來?只要這四個問題都能給出滿意的答案,那就是合格的計算結果。
回到 0.1 + 0.2。這不是程序的 bug,而是二進制與有限存儲共同作用的必然結果。
理解這個“誤差”,就是數值分析教給我們的最重要一課:
認識誤差,理解誤差,最終學會與誤差共存,并控制它。
????
參考資料:
[1]https://bbs.huaweicloud.com/blogs/287063
[2] https://www.puntoflotante.net/FLOATING-POINT-FORMAT-IEEE-754.htm
[3]https://www.h-schmidt.net/FloatConverter/IEEE754.html
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.