『壹』 浮點數原理與精度損失問題
碰巧最近定義介面的時候碰到了浮點數精度的問題,稍微整理了浮點數的一些知識點:
浮點數的表示借鑒了科學計數法,比如在十進制中311.56可以表示成 。類似地,浮點型數據的二進制存儲結構也可以被劃分成:符號位 + 指數位 + 尾數位。按照國際標准IEEE 754,任意一個二進制浮點數可以表示成:
其中:
舉兩個簡單的例子:
無論什麼數據,在計算機內存中都是以01存儲的,浮點數也不例外。
計算機中小數的表示按照小數點的位置是否固定可以分為浮點數和定點數。為了方便和float32浮點數做對比,我們構造一個32位精度的定點數,其中小數點固定在23bit處:
從定點數的存儲上看,它表示的數值范圍有限(以小數點在23bit為例,整數部分僅有8位,則整數部分取值范圍是0~255),但好在處理定點數計算的硬體比較簡單。
以32位浮點數為例,最高一位是符號位s,接著的8位是指數位E,最後的23位是有效數字M。double64最高一位是符號位,有11個指數位和52個有效數字位。下圖展示了float32類型的底層表示:
其中IEEE 754的規定為:
以78.375為例,它的整數和小數部分可以表示為:
因此二進制的科學計數法為:
一般而言轉換過程包括如下幾步:
按照前面IEEE 754的要求,它的底層存儲為:
十進制中的0.5(代表分數1/2)表示二進制中的0.1(等同於分數1/2),我們可以把十進制中的小數部分乘以2作為二進制的一位,然後繼續取小數部分乘以2作為下一位,直到不存在小數為止。以0.2這個無法精確表示成二進制的浮點數為例:
因此十進制下的0.2無法被精確表示成二進制小數,這也是為什麼十進制小數轉換成二進制小數時會出現精度損失的情況。
從二進制小數的科學計數法表示上看,可以知道float的精度為 ,double的精度為 。
[1] https://q.115.com/182920/T1268124.html
[2] https://blog.csdn.net/u014470361/article/details/79820892
[3] https://www.cnblogs.com/wangsiting1997/p/10677805.html
[4] https://blog.csdn.net/u014470361/article/details/79820892
[5] https://www.cnblogs.com/yiyide266/p/7987037.html
『貳』 請問浮點型數據在計算機是怎麼存儲的
對於浮點類型的數據採用單精度類型(float)和雙精度類型(double)來存儲,float數據佔用32bit,double數據佔用64bit。
無論是單精度還是雙精度在存儲中都分為三個部分:
1、符號位(Sign) : 0代表正,1代表為負。
2、指數位(Exponent):用於存儲科學計數法中的指數數據,並且採用移位存儲。
3、尾數部分(Mantissa):尾數部分。
(2)浮點數存儲原理csdn擴展閱讀
實型變數分為兩類:單精度型和雙精度型,
其類型說明符為float 單精度說明符,double
雙精度說明符。在Turbo
C中單精度型佔4個位元組(32位)內存空間,其數值范圍為3.4E-38~3.4E+38,只能提供七位有效數字。
雙精度型佔8
個位元組(64位)內存空間,其數值范圍為1.7E-308~1.7E+308,可提供16位有效數字。
實型變數說明的格式和書寫規則與整型相同。
例如: float x,y; (x,y為單精度實型量)
double a,b,c; (a,b,c為雙精度實型量)
實型常數不分單、雙精度,都按雙精度double型處理。
『叄』 計算機浮點數的儲存原理
浮點是以單元形式儲存在內存上的,但每個單元內存有限,所以比如你想輸入1/3的話,你以為是1/3了,實際上不足1/3,而是0.3333333333333333,所以計算時,會以0.3333333333333333的形式去計算,而不是1/3,因此出現了本來是0.6的,而輸出卻是0.599976.建議把浮點精度變大
『肆』 計算機組成原理里的:定點整數 定點小數 浮點數 編程里的基本數據類型int float在內存中的存儲
整型就是一般的存儲,有符號數,最高位為符號位,0表示正數,1表示負數。 無符號數,就沒有什麼格式了。
浮點數,就比較復雜了,它是遵守的IEEE754浮點編碼標准,拿FLOAT類型來說,這種類型是32位的,其中1位表示符號位,8位表示指數位,23位表示有效數字位。 簡單的用公式表示:(-1)^S * M ^e。 S是符號位,M是有效數字,E是指數,你最好自己去搜索一下IEEE754浮點數編碼的內容。
當然這種知識了解一下就好了。。參考資料推薦 :深入理解計算機系統 我記得是第二章中,有詳細的介紹。
『伍』 C/C++浮點數在內存中是怎麼存儲的
把浮點數的絕對值的二進製表達的小數點移動到從左至右數第1個「1」之後,捨去1和小數點,把剩餘的原碼二進制0、1序列從左至右截取23(float型)或52(double型)位作為尾數。
在尾數前添加8(float型)或11(double型)位用移碼表示「製造」尾數時小數點移動的位數,叫階碼;階碼的最左那一位表示小數點移動的方向。
在階碼前添加1位表示整個浮點數的正負,0表示大於等於0,1表示小於0。
把這一串0、1序列在小端機上由右至左存儲在某個地址開始的連續內存單元中,這「某個地址」就是承載這個浮點型數據的變數的地址。若在大端機上則將這一串0、1序列由左至右存放。
『陸』 浮點型數據在內存中實際的存放形式(儲存形式)
浮點型數據在內存中存儲不是按補碼形式,是按階碼的方式存儲,所以雖然int和float都是佔用了4個位元組,如果開始存的是int型數據,比如是個25,那麼用浮點的方式輸出就不是25.0,也許就變的面目全非。
你可以用共用體的方式驗證一下。在公用體中定義一個整形成員變數和一個浮點型成員變數,給整形賦值25,輸出浮點成員變數,你就知道了。
『柒』 float變數在內存當中是怎樣存儲的或是怎樣的一種存儲格式
浮點型變數在計算機內存中佔用4位元組(Byte),即32-bit。遵循IEEE-754格式標准。
一個浮點數由2部分組成:底數m 和 指數e。
±mantissa × 2exponent
(注意,公式中的mantissa 和 exponent使用二進製表示)
底數部分使用2進制數來表示此浮點數的實際值。
指數部分佔用8-bit的二進制數,可表示數值范圍為0-255。但是指數應可正可負,所以IEEE規定,此處算出的次方須減去127才是真正的指數。所以float的指數可從 -126到128.
底數部分實際是佔用24-bit的一個值,由於其最高位始終為 1 ,所以最高位省去不存儲,在存儲中只有23-bit。
到目前為止, 底數部分 23位 加上指數部分 8位 使用了31位。那麼前面說過,float是佔用4個位元組即32-bit,那麼還有一位是幹嘛用的呢? 還有一位,其實就是4位元組中的最高位,用來指示浮點數的正負,當最高位是1時,為負數,最高位是0時,為正數。
浮點數據就是按下表的格式存儲在4個位元組中:
Address+0 Address+1 Address+2 Address+3
Contents SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM S: 表示浮點數正負,1為負數,0為正數
E: 指數加上127後的值的二進制數
M: 24-bit的底數(只存儲23-bit)
主意:這里有個特例,浮點數 為0時,指數和底數都為0,但此前的公式不成立。因為2的0次方為1,所以,0是個特例。當然,這個特例也不用認為去干擾,編譯器會自動去識別。
通過上面的格式,我們下面舉例看下-12.5在計算機中存儲的具體數據:
Address+0 Address+1 Address+2 Address+3
Contents 0xC1 0x48 0x00 0x00 接下來我們驗證下上面的數據表示的到底是不是-12.5,從而也看下它的轉換過程。
由於浮點數不是以直接格式存儲,他有幾部分組成,所以要轉換浮點數,首先要把各部分的值分離出來。
Address+0 Address+1 Address+2 Address+3
格式 SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM
二進制 11000001 01001000 00000000 00000000
16進制 C1 48 00 00
可見:
S: 為1,是個負數。
E:為 10000010 轉為10進制為130,130-127=3,即實際指數部分為3.
M:為 10010000000000000000000。 這里,在底數左邊省略存儲了一個1,使用 實際底數表示為 1.10010000000000000000000
到此,我們吧三個部分的值都拎出來了,現在,我們通過指數部分E的值來調整底數部分M的值。調整方法為:如果指數E為負數,底數的小數點向左移,如果指數E為正數,底數的小數點向右移。小數點移動的位數由指數E的絕對值決定。
這里,E為正3,使用向右移3為即得:
1100.10000000000000000000
至次,這個結果就是12.5的二進制浮點數,將他換算成10進制數就看到12.5了,如何轉換,看下面:
小數點左邊的1100 表示為 (1 × 23) + (1 × 22) + (0 × 21) + (0 × 20), 其結果為 12 。
小數點右邊的 .100… 表示為 (1 × 2-1) + (0 × 2-2) + (0 × 2-3) + ... ,其結果為.5 。
以上二值的和為12.5, 由於S 為1,使用為負數,即-12.5 。
所以,16進制 0XC1480000 是浮點數 -12.5 。
上面是如何將計算機存儲中的二進制數如何轉換成實際浮點數,下面看下如何將一浮點數裝換成計算機存儲格式中的二進制數。
舉例將17.625換算成 float型。
首先,將17.625換算成二進制位:10001.101 ( 0.625 = 0.5+0.125, 0.5即 1/2, 0.125即 1/8 如果不會將小數部分轉換成二進制,請參考其他書籍。) 再將 10001.101 向右移,直到小數點前只剩一位 成了 1.0001101 x 2的4次方(因為右移了4位)。此時 我們的底數M和指數E就出來了:
底數部分M,因為小數點前必為1,所以IEEE規定只記錄小數點後的就好,所以此處底數為 0001101 。
指數部分E,實際為4,但須加上127,固為131,即二進制數 10000011
符號部分S,由於是正數,所以S為0.
綜上所述,17.625的 float 存儲格式就是:
0 10000011 00011010000000000000000
轉換成16進制:0x41 8D 00 00
所以,一看,還是佔用了4個位元組。
下面,我做了個有趣的實驗,就是由用戶輸入一個浮點數,程序將這個浮點數在計算機中存儲的二進制直接輸出,來看看我們上面所將的那些是否正確。
有興趣同學可以到VC6.0中去試試~!
#include<iostream.h>
#define uchar unsigned char
void binary_print(uchar c)
{
for(int i = 0; i < 8; ++i)
{
if((c << i) & 0x80)
cout << '1';
else
cout << '0';
}
cout << ' ';
}
void main()
{
float a;
uchar c_save[4];
uchar i;
void *f;
f = &a;
cout<<"請輸入一個浮點數:";
cin>>a;
cout<<endl;
for(i=0;i<4;i++)
{
c_save[i] = *((uchar*)f+i);
}
cout<<"此浮點數在計算機內存中儲存格式如下:"<<endl;
for(i=4;i!=0;i--)
binary_print(c_save[i-1]);
cout<<endl;
}
好了,我想如果你仔細看完了以上內容,你現在對浮點數算是能比較深入的了解了。
『捌』 單片機里浮點數是怎麼存放的
可以這么說:任何存儲器,無論是pc機,單片機,甚至內存卡的基本存儲模塊都是一樣
的結構(當然是對於ram而言),都是一個存儲單元對應地址線的一種組合相應存儲一個位元組,物理結構是裡面的八個觸發器,每個觸發器對應一個位元組。至於浮點數和整型數理論上沒什麼區別了把,就在多一個位元組存放小數點吧。
『玖』 說明用浮點數表示數據的原理
為了表示浮點數,數被分為兩部分:整數部分和小數部分.例如,浮點數14.234就有整數部分14和小數部分0.234.首先把浮點數轉換成二進制數,步驟如下:1把整數部分轉換成二進制.2把小數部分轉換成二進制.3在兩部分之間加上小數點.浮點數還可以規范化,浮點數可以用單精度表示法和雙精度表示法.規范化只存儲這個數的三個部分的信息:符號,指教和尾數.如+1000111.0101規范化後為
+ 2^6 * 1.0001110101
符號 指數 尾數
規范化數的單精度表示法如+2^6*1.01000111001解:
由於符號為正,就用0表示.指數是6,在Excess_127表示法中,給指數加上127得到133.用二進製表示,就是10000101.尾數是01000111001.當把位數增加到32位,得到01000111001000000000000.注意不可以漏掉左邊的0,因為它是小數.漏掉了那個0就相當於把這個數乘於2.這個數在內存中以32位數存儲.如下所示
符號 指數 尾數
0 10000101 01000111001000000000000
『拾』 浮點類型是如何存儲的
計算機中最小的存儲單位是bit只能保存0和1,整數在內存中如何存儲我們都知道,將要存儲的數字轉成2進制即可
用windows自帶的計數器可以方便的查看整數對應的2進制值
如:
byte類型(單位元組)
那浮點類型是如何用這么少的位元組(如float 4位元組)表示這么大(float 最大 3.4028235E38)的數字呢?
浮點數,是屬於有理數中某特定子集的數的數字表示,在計算機中用以近似表示任意某個實數。具體的說,這個實數由一個整數或定點數(即尾數)乘以某個基數(計算機中通常是2)的整數次冪得到,這種表示方法類似於基數為10的科學計數法。
科學計數法是一種記數的方法。把一個數表示成a與10的n次冪相乘的形式(1≤|a|<10,a不為分數形式,n為整數),這種記數法叫做科學計數法。當我們要標記或運算某個較大或較小且位數較多時,用科學計數法免去浪費很多空間和時間。
這也是一種目前最常用的浮點數標准!為許多CPU與浮點運算器所採用。
簡單的說就是將一個浮點數字拆成3個部分(符號部分、指數部分、小數部分) 存儲在連續的bit中,類似科學計數法。
用 {S,E,M}來表示一個數 V 的,即 V =(-1)S × M × 2E ,如下:
其中:
其中d.dd...d 為有效數字,β為基數,e 為指數
有效數字中 數字的個數 稱為 精度 ,我們可以用 p 來表示,即可稱為 p 位有效數字精度。
每個數字 d 介於 0 和基數 β 之間,包括 0。
對十進制的浮點數,即基數 β 等於 10 的浮點數而言,上面的表達式非常容易理解。
如 12.34,我們可以根據上面的表達式表達為:
1×10 1 + 2×10 0 + 3×10 -1 + 4×10 -2
其規范的浮點數表達為: 1.234×10 1 。
但對二進制來說,上面的表達式同樣可以簡單地表達。
唯一不同之處在於:二進制的 β 等於 2,而每個數字 d 只能在 0 和 1 之間取值。
如二進制數 1001.101 ,我們可以根據上面的表達式表達為:
1×2 3 + 0×2 2 + 0×2 1 + 1×2 0 + 1×2 -1 + 0×2 -2 + 1×2 -3
其規范浮點數表達為: 1.001101×2 3 。
二進制數 1001.101 轉成十進制如下:
由上面的等式,我們可以得出:
向左移動二進制小數點一位相當於這個數除以 2,而向右移動二進制小數點一位相當於這個數乘以 2。
如 101.11 = 5又3/4 (5.75),向左移動一位,得到 10.111 = 2又7/8 (2.875)。
除此之外,我們還可以得到這樣一個基本規律:
一個十進制小數要能用浮點數精確地表示,最後一位必須是 5(當然這是必要條件,並非充分條件)。
如下面的示例所示:
基本換算方法:
將10進制的數拆分成整數和小數兩個部分
整數部分除以2,取余數;小數部分乘以2,取整數位。
示例:
將十進制 1.1 轉成 二進制
整數部分:1
1
小數部分:0.1
二進制形式表示為:
1.000110011001100110011...
再加上整數1,約等於:
1.099609375
計算的位數越多越精確
注意:
二進制小數不像整數一樣,只要位數足夠,它就可以表示所有整數。
在有限長度的編碼中,二進制小數一般無法精確的表示任意小數,比如十進制小數0.2,我們並不能將其准確的表示為一個二進制數,只能增加二進制長度提高表示的精度。
根據 IEEE 754 浮點「雙精度格式」位布局。
如果參數是正無窮大,則結果為 0x7ff0000000000000L。
如果參數是負無窮大,則結果為 0xfff0000000000000L。
如果參數是 NaN,則結果為 0x7ff8000000000000L。
根據 IEEE 754 浮點「單一格式」位布局。
如果參數為正無窮大,則結果為 0x7f800000。
如果參數為負無窮大,則結果為 0xff800000。
如果參數為 NaN,則結果為 0x7fc00000。
這里以 double類型說明
將一個浮點數與上面的掩碼進行與運算,即可得到對應的 符號位、指數位、尾數位 的值。
1.000110011001100110011...
所以存為:
0 01111111111 000110011001100110011...
根據 IEEE 754 規范
在二進制,第一個有效數字必定是「1」,因此這個「1」並不會存儲。
單精和雙精浮點數的有效數字分別是有存儲的23和52個位,加上最左邊沒有存儲的第1個位,即是24和53個位。
通過計算其能表示的最大值,換十進制來看其精度:
浮點運算很少是精確的,只要是超過精度能表示的范圍就會產生誤差。而往往產生誤差不是因為數的大小,而是因為數的精度。
我自己理解為分兩種情況(這個不一定是對)
通過上面的轉換示例,我們知道小數的二進製表示一般都不是精確的,在有限的精度下只能盡量的表示近似值
值本身就不是精確的,再進行計算就很可能產生誤差
輸出:
0.1
原始值: 0 01111111011
指數:1019 -1023 = -4
二進制形式:
0.0001
0.2
原始值:0 01111111100
指數:1020 -1023 = -3
二進制形式:
0.00
0.3
原始值:0 01111111101
指數:1021 = -2
二進制形式:
0.00
二進制加法運算
這里用float驗證,float最大的精度是8位數
對於不能精確的表示的數,採取一種系統的方法:找到「最接近」的匹配值,它可以用期望的浮點形式表現出來,這就是舍入。
對於舍入,可以有很多種規則,可以向上舍入,向下舍入,向偶數舍入。如果我們只採用前兩種中的一種,就會造成平均數過大或者過小,實際上這時候就是引入了統計偏差。如果是採用偶數舍入,則有一半的機會是向上舍入,一半的機會是向下舍入,這樣子可以避免統計偏差。而 IEEE 754 就是採用向最近偶數舍入(round to nearest even)的規則。
(這段是網上抄的)
這里以java語言示例,用大端的方式示例(網路序)
java中是以大端模式存儲的,java對我們屏蔽了內部位元組順序的問題以實現跨平台!
實際在不同的cpu架構下,存儲方式不同,我們常用的X86是以小端的模式存儲的。
網路傳輸一般採用大端序,也被稱之為網路位元組序,或網路序。IP協議中定義大端序為網路位元組序。
輸出: