1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        漫話:如何給女朋友解釋為什么計算機中 0.2 + 0.1 不等于 0.3 ?

        共 2974字,需瀏覽 6分鐘

         ·

        2020-11-07 01:49



        為什么當(dāng)我們使用電腦瀏覽器計算0.2+0.1的時候,解決卻是0.30000000000000004,而0.1+0.6的結(jié)果卻是0.7呢?

        這個問題其實一直是一個經(jīng)典的問題,甚至有一個網(wǎng)站的域名就是https://0.30000000000000004.com/ ,主要就是解釋這個問題的。

        在這個網(wǎng)站中,列舉了各種編程語言中計算0.2+0.1的結(jié)果,摘選幾個如下:

        ?

        可以看到,在各種語言中,計算0.2+0.1的結(jié)果都出奇的一致,那就是這個神奇的0.30000000000000004。

        其實,當(dāng)我們使用瀏覽器的控制臺(F12)進行計算的時候,用到的就是JavaScript語言進行計算的,所以,前面的現(xiàn)象,歸根結(jié)底其實和具體的編程語言無關(guān)。

        主要問題還是計算機中到底是如何表示小數(shù)以及如何進行小數(shù)運算的。

        我們知道,計算機只認識0和1 [為什么計算機只認識0和1],現(xiàn)實世界中的內(nèi)容想要通過計算機存儲、計算或者展示,都需要轉(zhuǎn)換2進制。在現(xiàn)實世界中,數(shù)字主要有整數(shù)和小數(shù)兩種。

        在之前的[為什么計算機用補碼存儲數(shù)據(jù)]這篇文章中,我們介紹過,計算機中表示整數(shù)的方式有很多,如原碼、反碼以及補碼等。

        整數(shù)包括正整數(shù)、負整數(shù)以及零。在計算機中存儲的整數(shù)則分為有符號數(shù)和無符號數(shù)。

        對于無符號數(shù),采用哪種編碼方式都無所謂,對于有符號數(shù)的編碼方式,常用的是補碼。

        那么,一個十進制數(shù)字想要獲得其二進制的補碼,需要先通過一定的算法得到他對應(yīng)的原碼。


        十進制轉(zhuǎn)二進制

        首先我們看一下,如何把十進制整數(shù)轉(zhuǎn)換成二進制整數(shù)?

        十進制整數(shù)轉(zhuǎn)換為二進制整數(shù)采用"除2取余,逆序排列"法。

        具體做法是:

        • 用2整除十進制整數(shù),可以得到一個商和余數(shù);

        • 再用2去除商,又會得到一個商和余數(shù),如此進行,直到商為小于1時為止

        • 然后把先得到的余數(shù)作為二進制數(shù)的低位有效位,后得到的余數(shù)作為二進制數(shù)的高位有效位,依次排列起來。

        如,我們想要把127轉(zhuǎn)換成二進制,做法如下:

        那么,十進制小數(shù)轉(zhuǎn)換成二進制小數(shù),又該如何計算呢?

        十進制小數(shù)轉(zhuǎn)換成二進制小數(shù)采用"乘2取整,順序排列"法。

        具體做法是:* 用2乘十進制小數(shù),可以得到積 * 將積的整數(shù)部分取出,再用2乘余下的小數(shù)部分,又得到一個積 * 再將積的整數(shù)部分取出,如此進行,直到積中的小數(shù)部分為零,此時0或1為二進制的最后一位?;蛘哌_到所要求的精度為止。

        所以,十進制的0.625對應(yīng)的二進制就是0.101。


        不是所有數(shù)都能用二進制表示

        我們知道了如何將一個十進制小數(shù)轉(zhuǎn)換成二進制,那么是不是計算就可以直接用二進制表示小數(shù)了呢?

        前面我們的例子中0.625是一個特列,那么還是用同樣的算法,請計算下0.1對應(yīng)的二進制是多少?

        我們發(fā)現(xiàn),0.1的二進制表示中出現(xiàn)了無限循環(huán)的情況,也就是(0.1)10 = (0.000110011001100…)2

        這種情況,計算機就沒辦法用二進制精確的表示0.1了。

        也就是說,對于像0.1這種數(shù)字,我們是沒辦法將他轉(zhuǎn)換成一個確定的二進制數(shù)的。


        IEEE 754

        為了解決部分小數(shù)無法使用二進制精確表示的問題,于是就有了IEEE 754規(guī)范。

        IEEE二進制浮點數(shù)算術(shù)標(biāo)準(zhǔn)(IEEE 754)是20世紀(jì)80年代以來最廣泛使用的浮點數(shù)運算標(biāo)準(zhǔn),為許多CPU與浮點運算器所采用。

        浮點數(shù)和小數(shù)并不是完全一樣的,計算機中小數(shù)的表示法,其實有定點和浮點兩種。因為在位數(shù)相同的情況下,定點數(shù)的表示范圍要比浮點數(shù)小。所以在計算機科學(xué)中,使用浮點數(shù)來表示實數(shù)的近似值。

        IEEE 754規(guī)定了四種表示浮點數(shù)值的方式:單精確度(32位)、雙精確度(64位)、延伸單精確度(43比特以上,很少使用)與延伸雙精確度(79比特以上,通常以80位實現(xiàn))。

        其中最常用的就是32位單精度浮點數(shù)和64位雙精度浮點數(shù)。

        IEEE并沒有解決小數(shù)無法精確表示的問題,只是提出了一種使用近似值表示小數(shù)的方式,并且引入了精度的概念。

        浮點數(shù)是一串0和1構(gòu)成的位序列(bit sequence),從邏輯上用三元組{S,E,M}表示一個數(shù)N,如下圖所示:

        • S(sign)表示N的符號位。對應(yīng)值s滿足:n>0時,s=0; n≤0時,s=1。

        • E(exponent)表示N的指數(shù)位,位于S和M之間的若干位。對應(yīng)值e值也可正可負。

        • M(mantissa)表示N的尾數(shù)位,恰好,它位于N末尾。M也叫有效數(shù)字位(significand)、系數(shù)位(coefficient), 甚至被稱作"小數(shù)"。

        則浮點數(shù)N的實際值n由下方的式子表示:

        上面這個公式看起來很復(fù)雜,其中符號位和尾數(shù)位還比較容易理解,但是這個指數(shù)位就不是那么容易理解了。

        其實,大家也不用太過于糾結(jié)這個公式,大家只需要知道對于單精度浮點數(shù),最多只能用32位字符表示一個數(shù)字,雙精度浮點數(shù)最多只能用64位來表示一個數(shù)字。

        而對于那些無限循環(huán)的二進制數(shù)來說,計算機采用浮點數(shù)的方式保留了一定的有效數(shù)字,那么這個值只能是近似值,不可能是真實值。

        至于一個數(shù)對應(yīng)的IEEE 754浮點數(shù)應(yīng)該如何計算,不是本文的重點,這里就不再贅述了,過程還是比較復(fù)雜的,需要進行對階、尾數(shù)求和、規(guī)格化、舍入以及溢出判斷等。

        但是這些其實不需要了解的太詳細,我們只需要知道,小數(shù)在計算機中的表示是近似數(shù),并不是真實值。根據(jù)精度不同,近似程度也有所不同。

        如0.1這個小數(shù),他對應(yīng)的在雙精度浮點數(shù)的二進制為:0.00011001100110011001100110011001100110011001100110011001 。

        0.2這個小數(shù)0.00110011001100110011001100110011001100110011001100110011 。

        所以兩者相加:

        轉(zhuǎn)換成10進制之后得到:0.30000000000000004!


        避免精度丟失

        在Java中,使用float表示單精度浮點數(shù),double表示雙精度浮點數(shù),表示的都是近似值。

        所以,在Java代碼中,千萬不要使用float或者double來進行高精度運算,尤其是金額運算,否則就很容易產(chǎn)生資損問題。

        為了解決這樣的精度問題,Java中提供了BigDecimal來進行精確運算。


        參考資料:

        https://0.30000000000000004.com/

        https://zh.wikipedia.org/zh-hans/IEEE_754

        https://www.h-schmidt.net/FloatConverter/IEEE754.html


        良許個人微信


        添加良許個人微信即送3套程序員必讀資料


        → 精選技術(shù)資料共享

        → 高手如云交流社群





        本公眾號全部博文已整理成一個目錄,請在公眾號里回復(fù)「m」獲??!

        推薦閱讀:

        linux系統(tǒng)服務(wù)(systemctl)的使用

        趣文:用錘子發(fā)射炮彈,程序員的世界你不懂

        Linux 的文件系統(tǒng)及文件緩存知識點整理


        5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機,樹莓派,等等。在公眾號內(nèi)回復(fù)「1024」,即可免費獲?。。?/span>


        瀏覽 34
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            青青草狠狠操 | 操大奶熟女 | 久久久久成人片免费观看蜜芽 | 国产午夜精品一区二区三区视频 | 久久网止| 日韩操逼一区 | 成年人视频在线观看免费 | 哪个网站可以看毛片 | 操烂骚逼| 天堂网成人 |