色色一区二区三区,一本大道道久久九九AV综合,国产香蕉97碰碰视频va碰碰看,综合亚洲国产2020

    <legend id="mljv4"><u id="mljv4"><blockquote id="mljv4"></blockquote></u></legend>

    <sub id="mljv4"><ol id="mljv4"><abbr id="mljv4"></abbr></ol></sub>
      <mark id="mljv4"></mark>
      大數(shù)據(jù) > 你真的知道0.1+0.2為何不等于0.3嗎?

      你真的知道0.1+0.2為何不等于0.3嗎?

      2019-09-07 04:20閱讀(59)

      " 打開chrome控制臺,給一個特別簡單的輸入如下: 0.1 + 0.2 // 0.30000000000000004 復制代碼 不知道你有沒有吃驚,這么簡單的一個計算,無論在js中還是在pytho

      1
      "

      打開chrome控制臺,給一個特別簡單的輸入如下:

      0.1 + 0.2 // 0.30000000000000004 
      復制代碼

      不知道你有沒有吃驚,這么簡單的一個計算,無論在js中還是在python中,都不是準確的0.3,這是為什么呢?

      緣起

      要了解這個問題,首先我們需要知道浮點數(shù)在計算機中到底是如何進行存儲的?不知道你是怎么想的,總之我開始的第一反應就是假設是32位的存儲空間,我可能會按照整數(shù)的存儲方式去想象,比如1-24位是整數(shù)位,剩余的8位代表小數(shù),這樣可以嗎?當然是可以的,但是先考慮下下面的這個問題:

      你真的知道0.1+0.2為何不等于0.3嗎?

      想象紅色區(qū)域是所能放置的數(shù)字的最大空間,現(xiàn)在有個問題,當我們想繼續(xù)加0的時候,發(fā)現(xiàn)放不下了,因為空間是有限的,這個時候,我們會怎么辦?

      你真的知道0.1+0.2為何不等于0.3嗎?

      對,沒錯,科學計數(shù)法,就是我們在學習過程中,如果位數(shù)太多,我們一般都會用科學計數(shù)法來表示,這樣的好處是,書寫的位數(shù)小,表示的位數(shù)多,所以,回到計算機中,32位來表示實數(shù)的話,最多能表示多少位?2^32次方個,大約就是40億,40億數(shù)字很多嗎?多,但是和無限多的實數(shù)集來比,滄海一粟,不夠看的,所以計算機的設計者就要考慮這個問題了,如何讓計算放下更多的數(shù)字?

      真的有“定點數(shù)”

      還記得上面說的,1-24表示整數(shù)位,剩余的表示小數(shù)位嗎?這種存儲方式就叫定點數(shù),1-24位每4位表示一個0~9的數(shù)字的話,可以有6位表示整數(shù)部分,剩余2位表示小數(shù)部分,這樣我們可以用32位表示從0到999999.99這樣1億個實數(shù),這種用2進制來表示10進制的方式,叫做BCD編碼(Binary-Coded Decimal),比如說8421碼,從左往右的權依次是8,4,2,1,等等,有興趣的可以去了解一下。

      “定點數(shù)”存在哪些問題

      定點數(shù)有幾個明顯的缺點:

      占了很大的位數(shù),但是能表示的數(shù)字范圍卻是有限的;無法同時表示很大的數(shù)字和很小的數(shù)字

      其實究其根本原因,還是這種方式的“有限”限制了它,那么有沒有一種方式,可以讓32位所能表示的數(shù)字,更“無限”一點,更適合我們的訴求?

      當然,設計計算機的前輩智慧是無限的~

      浮點數(shù)是如何表示的

      就像使用科學計數(shù)法一樣,計算機前輩在浮點數(shù)的設計中也用了一樣的思想,IEEE的標準定義了2個基本的浮點數(shù)格式,一個是32位的單精度浮點數(shù),一個是64位的雙精度浮點數(shù),也就是float或float32和double或float64這兩個數(shù)據(jù)格式,雙精度和單精度的表示形式是差不多的,我們以單精度的作為了解和學習。

      你真的知道0.1+0.2為何不等于0.3嗎?

      分為3部分:

        第一部分是符號位,用s表示,代表正負,要記住的是在浮點數(shù)的范圍內(nèi),所有數(shù)字都是有符號的;第二部分是指數(shù)位,用e表示,代表指數(shù),用8位bit表示的數(shù)字范圍是0~255,為了同時表示大數(shù)和小數(shù),我們把0~255去掉頭尾(0,255后面會用到)的1~254去映射到-126~127,這樣同時可以表示最大最小數(shù)字;第三部分是有效數(shù)位,用f表示,代表的是有效的數(shù)位;

      綜合上述表示和科學計數(shù)法,我們的浮點數(shù)就可以表示為公式

      (-1)^s * 1.f * 2^e

      看完公式有沒有發(fā)現(xiàn)問題?你會發(fā)現(xiàn),我們這個公式無法表示0,的確,這是一個巧妙的設計,我們用0(8個bit都為0)和255(8個bit都為1)來表示一些特殊的數(shù)值,可以認為他們2個是特殊的flag位,比如當e和f都為0的時候,我們就認為這個浮點數(shù)是0,看下表:

      你真的知道0.1+0.2為何不等于0.3嗎?

      以0.5為例,0.5的符號位s是0,f也是0,e是-1,

      這樣(-1)^0 * 1.0 * 2 ^ -1 = 0.5

      用32位bit表示就是

      s e f 0 0111 1110 0000 ...0 1位 8位 23位 0.5 通過這樣的表示方式,可以明顯的發(fā)現(xiàn)32位所能表示的實數(shù)范圍是很大的,又因為這種方式創(chuàng)建的實數(shù)中小數(shù)點的位置是可以”浮動“的,所以也被叫做浮點數(shù),

      到這里我們知道了浮點數(shù)是怎么存儲的了,但是還沒解決我們開始的問題,為何0.1+0.2!=0.3,首先我們要知道0.1是怎么存儲的:

      (-1)^s * 1.f * 2^e = 0.1

      求解e

      s=0 f=0 e=Math.log2(0.1) // -3.321928094887362

      可以看出來這里0.1是算不出來一個準確數(shù)字的,從0.1到0.9只有0.5是可以求出一個準確的值的,剩下的都算不出來一個準確的值,這也就是為什么0.1+0.2會導致的精度問題,也就是說浮點數(shù)無論是表示還是計算其實都是近似計算,而近似計算就一定會導致一些問題,比如,你希望銀行給你存錢以及算利息的時候用浮點數(shù)計算嗎?當然不希望,否則你的錢算多了還好,算少了豈不是虧大了~

      浮點數(shù)&amp;二進制

      把一個二進制表示的浮點數(shù)(0.1001),轉為10進制表示,因為小數(shù)點后的每一位都表示的是2的-N次方,因此轉為10進制就是:

      (1 * 2 ^ -1) + (0 * 2 ^ -2) + (0 * 2 ^ -3) + (1 * 2 ^ -4) = 0.5625

      可以理解為,對于二進制轉十進制來說,從小數(shù)點開始,往左就是把2的指數(shù)從0開始過一位+1,包括0,往右就是從-1開始依次-1。

      把一個10進制的浮點數(shù),轉為二進制的話,和整數(shù)的二進制表示采用“除以 2,然后看余數(shù)”的方式相比,小數(shù)部分轉換是用一個相似的反方向操作,就是乘以2,然后看是否大于1,如果大于1就記下1并把結果減去1,一直重復操作。

      比如,十進制的9.1,小數(shù)部分0.1轉為2進制的過程為:

      你真的知道0.1+0.2為何不等于0.3嗎?

      這是得到一個無限循環(huán)的部分”0011“,整數(shù)部分9轉為二進制就是1001,因此結果就是1001.000110011...

      把小數(shù)點做移3位,得到一個浮點數(shù)的結果是 1.001000110011... * 2 ^ 3

      找到我們上面的公式 (-1)^s * 1.f * 2^e 套公式可得到:

      s = 0 f = 00100011001100110011 001(到23位后自動舍棄,因為最長只能放23位有效數(shù)字)

      指數(shù)位是3,我們e的范圍是1-254 對半分正數(shù)和負數(shù),所以127表示0,從127開始加3,得到結果是130,130轉為二進制表示結果就是: 1000 0010, 所以得到e=1000 0010, 結果如下:

      你真的知道0.1+0.2為何不等于0.3嗎?

      所以最終的二進制表示結果是: 0100 0001 0001 0001 1001 1001 1001 1001

      如果我們再把這個浮點數(shù)表示換算成十進制, 實際得到的準確值是 9.09999942779541015625。相信你現(xiàn)在應該不會感覺奇怪了。

      小心你的“存款”

      首先,我們了解一下浮點數(shù)的加法計算過程是怎么樣的,拿0.5 + 0.125來做計算,首先0.5套用公式計算結果是:

      s = 0 有效位1.f = 1.0000... e = -1;

      0.125 轉換為:

      s = 0 有效位1.f = 1.0000... e = -3;

      然后,計算口訣是 指數(shù)位先對齊(小轉大,這里要把e統(tǒng)一為-1), 然后按位相加符號位和有效位,e保持統(tǒng)一后的結果,因此:

      符號位s 指數(shù)位e 有效位1.f 0.5 0 -1 1.0 0.125 0 -3 1.0 0.125對齊指數(shù)位 0 -1 0.01 0.5 + 0.125 0 -1 1.01 結果就是 (-1)^0 * 1.25 * 2^-1 = 0.625;

      ps: 為啥是1.25?雖然我們計算得出的是1.01 但是不要忘記計算是通過2進制算的,計算十進制的時候要轉回來哦,所以0100000.... 后面都是0不用管,小數(shù)部分,從頭開始乘以2的-N次別忘了,所以結果就是2^-2 = 0.25 加上整數(shù)位的1 就是1.25了~

      可以發(fā)現(xiàn),其實浮點數(shù)的計算過程,通過一個加法器也是可以實現(xiàn)的,電路成本同樣不會很高,但是需要注意一些別的問題:

      計算過程中,需要先對齊,但是有效數(shù)位的長度是23位,假如有一個很大的數(shù)字和一個很小的數(shù)字進行相加,然后對齊的過程中,小數(shù)被0部位過程中直接溢出了,23位不夠用了,就會出現(xiàn)問題,補完后一些有效位被丟掉了,從而導致結果上的誤差,兩個數(shù)的指數(shù)位差超過23,比如到2^24位(差不多1600萬倍),這2個數(shù)相加后,結果就直接是較大數(shù),較小數(shù)完全被拋棄了。。。

      有些同學會急急忙忙去chrome的控制輸入下面的代碼:

      Math.pow(2, 24) + 0.1 // 16777216.1 
      復制代碼

      騙人,結果不是還有0.1嗎,別急,小伙伴,js內(nèi)置的Number是64位的,你可以試試

      Math.pow(2, 50) + 0.1 // 1125899906842624
      復制代碼

      是不是小數(shù)沒了?【這種現(xiàn)象也叫大數(shù)吃小數(shù)】

      所以如果銀行采用IEEE-754 32位的浮點數(shù)計數(shù)方法來保管存款的話,假設你是一個大老板,你的賬戶中有2000萬rmb,這個時候你的某一個員工給你打了1塊錢,哈哈對不起,銀行給算丟了,你的存款是不變的!所以,一般銀行啊,電商一類的都會在涉及到錢的時候使用定點數(shù)或者整數(shù)來計算,避免出現(xiàn)精度丟失的問題,如果你去銀行涉及數(shù)據(jù)庫,一定要小心謹慎~

      總結

      這篇文章我們從浮點數(shù)的表示開始,到存儲,到轉換以及計算過程分析了真實的計算機世界中浮點數(shù)到底是怎么運行的,從中也了解了浮點數(shù)究竟為何會丟失精度:

        浮點數(shù)在存儲的時候可能出現(xiàn)不能準確轉化為對應2進制的情況在計算過程中,又存在大數(shù)吃小數(shù)的可能,也會導致數(shù)據(jù)不準確

      延伸

      精度丟失不是沒法解決的,有成熟的方案,不做過多介紹,有興趣大家可以去研究:

      Summation Formula 算法

      說明:

      文章內(nèi)容大部分參考自 徐文浩 老師的 「深入淺出計算機組成原理」專欄,加了一些自己的理解做了一個簡單的總結,之后還會繼續(xù)不定時的分享一些自己的所得,如果覺得還不錯,點個贊吧~

      ps: 有同學可能會問,既然只有0.5可以轉為一個準確的數(shù)字,為何0.1+0.1沒有問題,這個我還沒仔細研究,不過我猜想是因為本身計算就是一個計算近似值的過程,因此再得出結果后,如果還在一個近似范圍內(nèi),就會認為沒有誤差,超過這個范圍,則會認為出現(xiàn)誤差了,總之我們可以確認的是計算過程中拿到的確實是一個近似數(shù)了,這個也確實是導致一些浮點數(shù)計算丟失精度的原因~

      有興趣的話可以到這里查看實際的數(shù)字在計算機中存儲的具體內(nèi)容~

      你真的知道0.1+0.2為何不等于0.3嗎?

      "