日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python0.1+0.2不等于0.3_为什么0.1 + 0.2不等于0.3?

發布時間:2025/4/16 python 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python0.1+0.2不等于0.3_为什么0.1 + 0.2不等于0.3? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原標題:為什么0.1 + 0.2不等于0.3?

0.1 + 0.2不等于0.3這是一個普遍的問題,例如在JS控制臺輸入將得到0.30000000000000004

在python的控制臺也是輸出這個數:

在C里面運行以下代碼,指定輸出小數位為57位:

printf("%.57f", 0.1 + 0.2);

將得到:

0.300000000000000044408920985006261616945266723632812500000

那我們的問題來了,為什么計算機計算的0.1加0.2會不等于0.3?

首先我們來看一下JS能夠表示最大數是多少,如下所示,打印Number.MAX_SAFE_INTEGER和Number.MAX_VALUE:

JS能表示的最大整數為9e16,能表示的最大正數為1.79e308,這兩個數是怎么得來的呢?先來看一下整數在計算機的存儲方式。

我們知道計算機是使用二進制存儲數據的,整數也是同樣的道理,整數可以分成短整型、基本型、長整型,占用的存儲空間分別為16位、32位、64位,如果操作系統是32位的,那么使用長整型將會慢于短整型,因為一個數它需要分兩次取,而在64位的操作系統,一次就可以取到8個字節或64位的數據,所以使用長整型不會有性能問題。另外,32位的操作系統內存只能識別到2 ^ 32 = 4G,而現在的電腦內存動不動就是8G、16G,所以現在的電腦基本都是64位的,不比前幾年。

32位有符號整型的存儲方式如下圖所示:

第一位0表示正數,1表示負數,剩下的31位表示數值,所以32位有符號整數最大值為:

2 ^ 31 – 1 = 2147483647

即21億多,如果要表示全球人口,那么32位整型是不夠的。同理,64位有符號整型能表示的最大值為:

2 ^ 63 – 1 = 9223372036854775807

這是一個19位數,mysql數據庫的id字段就經常用長整型表示,那為什么JS能表示的最大整數只有16位,而不是19位呢?這個要先說一下浮點型在計算機的存儲方式。

現在浮點型的存儲實現基本按IEEE754標準,符點數分為單精度和雙精度,單精度為32位,雙精度為64位。

在十進制里面,一個小數如0.75可以表示成7.5 * 10 ^ -1,同樣地在二進制里面,0.75可以表示成:

0.75 = 1.1 * 2 ^ -1

即0.75 = (1 + 1 * 2 ^ -1) * 2 ^ -1,其中冪次方-1用階碼表示,而1.1由于二進制整數部分都是1,所以去掉1留下0.1作為尾數部分(因為都是1點多的形式,所以這個1就沒必要存了)。因此0.75在單精度浮點數是這樣表示的:

注意階碼要加上一個基數,這個基數為2 ^ (n – 1) – 1,n為階碼的位數,32位的階碼為8位,所以這個基數為127,8位階碼能表示的最小整數為0,最大整數為255,所以能表示的指數范圍為:(0 – 127) ~ (255 – 127)即-127~128,上面要表示指數為-1,需要加上基數127,就變成126,如上圖所示。

而尾數為0.1,所以尾數的最高位為1,后面的值填充0.

反過來,如果知道一個二進制的存儲方式,同樣地可以轉換成10進制,如上圖的計算結果應為:

(1 + 1 * 2 ^ – 1) * 2 ^ (126 – 127) = 1.5 * 2 ^ -1 = 0.75

那么0.1又該如何表示成一個二進制呢?

由于0.75 = 1 * 2 ^ -1 + 1 * 2 ^ -2,剛好可以被二進制精確表示,那0.1呢?沒辦法了,0.1無法被表示成這種形式,只能是用另外一個數盡可能地接近0.1(同理1/3無法在10進制精確表示,但是可以在3進制精確表示,只是我們習慣了10進制)。

我們可以用一小段來研究一下0.1被存儲成什么了,如下代碼所示:

voidprintBits(size_tconstsize,voidconst*constptr)

{

unsignedchar*b=(unsignedchar*)ptr;

unsignedcharbyte;

inti,j;

for(i=size-1;i>=0;i--)

{

for(j=7;j>=0;j--)

{

byte=(b[i]>>j)&1;

printf("%u",byte);

}

}

puts("");

}

doublea=0.1;

doubleb=0.1;

printBits(sizeof(a),&a);

printBits(sizeof(b),&b);

因為C可以讀取到原始的內存內容,所以可以打印每位的數據是什么。如上代碼打印的結果如下:

雙精度浮點數用11位表示階碼,52位表示尾數,如下圖所示:

所以雙精度的階碼基數為2 ^ 10 – 1 = 1023,0.1的階碼為01111111011,等于二進制1019,所以它的指數為-4:

尾數約為0.6:

由于這個精度不夠,我們要找一個高精度的計算器,如筆者找的:

有了這個尾數之后,再讓它乘以指數,得到結果為:

也就是說0.1的實際存儲要比0.1大,大概大了5.5e-17.

注意到,0.2和0.1的區別在于0.2比0.1的階碼大了1,其它的完全一樣。所以,0.2也是偏大了:

兩個數相加的結果為:

但是注意到0.1 + 0.2并不是上面的結果,要比上面的大:

這又是為什么呢?因為浮點數相加,需要先比較階碼是否一致,如果一致則尾數直接相加,如果不一致,需要先對階,小階往大階看齊,即把小階的指數調成和大階的一樣大,然后把它的尾數向右移相應的位數。如上面的0.1是小階,需要對它進行處理,如下:

需要把0.1的小數點向右移一位變成:

向右移一位導致尾數需要進行截斷,由于最后一位剛好是0,所以這里直接舍棄,如果是1,那么尾數加1,類似于十進制的四舍五入,避免誤差累積。現在0.1和0.2的階碼一樣了,尾數可以進行相加減了,如下把它們倆的尾數相加:

可以看到,發生了進位,變成了53位,已經超過了尾數52位的范圍,所以需要把階碼進一位,即指數加1,兩數和的尾數右移一位,即除以2,由于尾數的最后一位是1,進行“四舍五入”,即舍棄最后一位后再加上1,最后尾數變成了如下圖所示:

而指數加1,變成了-2,所以最后的計算結果為:

這個就和控制臺的輸出一致了,并且和C的輸出完全一致。到此,我們就回答了為什么0.1加0.2不等于0.3了。上面還提出了兩個問題,其中一個是:為什么JS的最大正數是1.79e308呢?這個數其實就是雙精度浮點數所能表示最大正值,如下使用python的輸出:

那為什么JS的最大正整數不是正常的64位的長整型所能表示的19位呢?因為JS的正整數是用的尾數的長度表示的,由于尾數是52位,加上整數的一位,它所能表示的最大的整數為:

為什么JS要用這種方式呢?因為JS的整型和浮點型在計算過程中可以隨時自動切換,應該是考慮到了這個原因,所以才拿浮點型的大小限制來做整型大小的限制。

由于后端的數據庫的ID字段可能會大于這個值,如果傳來了一個很大的數,在調JSON.parse的時候將會丟失精度,ID就不對了,所以如果出現這種情況,應該讓后端把ID當成字符串的方式傳給你。

另外需要注意的是,雙精度符點數的可靠位數為15位,也就是說從第16位開始可能是不對的,如0.1 + 0.2 = 0.30000000000000004,最后面的04這兩位是不可靠的。

但是會有一種情況精度要求很高,15位精度會不夠用,例如計算天體運算。那怎么辦呢?有一種絕對精準的方式就是用分數表示,例如0.1 + 0.2 = 3/10,計算的過程和最后的結果都用分數表示,分數的結果,你需要精確到多少位都可以取到。這個在matlab/maple等科學計算軟件都有實現。

最后怎么判斷兩個小數是否相等呢?用等號肯定是不行的了,判斷兩個小數是否相等要用它們的差值和一個很小的小數進行比較,如果小于這個小數,則認為兩者相等,ES6新增了一個Math.EPSILON屬性,如比較0.1 + 0.2是否等0.3應該這么操作:

作者:會編程的銀豬

原文:http://www.renfed.com/2017/05/13/float-number/

---- 廣告 ----

掘金是一個高質量的技術社區,從ECMA6到Vue.js,性能優化到開源類庫,讓你不錯過前端開發的每一個技術干貨。點擊鏈接即可訪問掘金官網,或到各大應用市場搜索「掘金」下載APP,技術干貨盡在掌握中返回搜狐,查看更多

責任編輯:

總結

以上是生活随笔為你收集整理的python0.1+0.2不等于0.3_为什么0.1 + 0.2不等于0.3?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。