float 和 double (我記憶體裡的浮點型資料?)
前面隨便說說~~~~~
二更,因為好幾個人因為這篇文章把我批鬥了,把有問題的地方修正。
今天看到一個問題
能不能用double去取代float?
前段時間,有個朋友問我,在java裡面我想把一個高精度的
浮點
型儲存下來,但是每次儲存的時候都會被強制降低精度,對於浮點型的理解,真的非常非常重要,特別對嵌入式軟體開發,或者演算法開發,涉及到資料類的,浮點型非常非常關鍵,就比如,微信為什麼不讓我發0。0000000001 元的
微信紅包
呢? 有沒有想過這個問題?如果這樣做對於他們伺服器後臺的運算能力要求非常高,so,~~~~~
所以想簡單寫一下,float和double的區別以及應用場景
1、浮點型的值範圍
float和double的範圍是由指數的
位數
來決定的。在VC++6。0中,float佔4個位元組,double佔8個位元組。
Type Storage size Value range
float
4 byte
1。2E-38 to 3。4E+38
double
8 byte
2。3E-308 to 1。7E+308
long double
10 byte
3。4E-4932 to 1。1E+4932
2、浮點型在記憶體裡面是如何儲存的?
我相信這個問題大家沒有好好的去考慮過,浮點型如何儲存,這才是我們討論浮點型的關鍵所在,關於上面的浮點型取值範圍,也是網上複製下來的,至於真實性,我覺得你們要看了這部分才可能真正理解浮點型,而且最好在自己的編譯器去測試,浮點型是可以等於負數的,所以上面的範圍,你們認為是正確的嗎?
我拿
float
來舉個栗子:
float在記憶體中的儲存方式
以下 部分如發現問題,請留言,會發小小紅包感謝,微信weiqifa0
首先使用基數2而不是基數10來表示
科學計數法
變體中的浮點值。例如,值3。14159可以表示為
1。570795 * 2^{1}
1。570795是
有效數字又名尾數(在上圖中指尾數部分)
; 它是包含有效數字的數字的一部分。此值乘以基數2,上升到1的冪,得到3。14159。
浮點數透過儲存
有效數和指數(以及符號位)
進行編碼。
典型的32位佈局如下所示:
3 32222222 22211111111110000000000
1 09876543 21098765432109876543210
+-+————+————————————-+
| | | |
+-+————+————————————-+
^ ^ ^
| | |
| | +—— 有效數
| |
| +——————————- 指數
|
+———————————— 符號位
與有符號整數型別一樣,高位表示符號;
0
表示
正
值,
1
表示
負
值。
而對於指數部分,因為指數可正可負,8位的指數位能表示的指數範圍就應該為:-127-128了, 所以指數部分的儲存採用移位儲存,
儲存的資料為元資料+127
。
舉例:
剩餘的位元用於有效數字。每個位表示從左側算起的
2的負冪,float一共23位
,舉例:
某些平臺假定有效數中的“隱藏”前導位始終設定為1,因此有效數中的值始終在[0。5,1之間]。這允許這些平臺以稍高的精度儲存值。
所以3.14159的值將表示為
0 10000000 10010010000111111010000
^ ^ ^
| | |
| | + ——-有效數= 1。570795 。。。
| |
| + ——————————-指數= 2(130 - 128)
|
+ ————————————- sign = 0(正面)
value = 1 (符號位) * 2 (指數位) *(有效數字)
值= 1 0 * 2^1 * 1。570795 。。。
值= 3。14159 ……
現在,如果你將有效數字中的所有位相加,你會注意到它們總共
不是3.14195
;
他們實際上
是3.141590118408203125,(小編實測資料)
。
沒有足夠的位來準確儲存值; 我們只能儲存一個
近似值
。有效數字中的位數決定了精度
23位給出了大約6位精度的十進位制數字
。64位浮點型別在有效位數中提供足夠的位,
可提供大約12到15位的精度
。但要注意,有一些數值不能被精確表示。就像1/3這樣的值不能用有限數量的十進位制數字表示,由於值是近似值,因此使用它們進行計算也是近似值,並且累積誤差會累積。
#include
void
main
(
void
)
{
for
(
float
i
=
0
;
i
<
1
;
i
+=
0。01
)
{
printf
(
“%f
\r\n
”
,
i
);
}
for
(
double
i
=
0
;
i
<
1
;
i
+=
0。01
)
{
printf
(
“%f
\r\n
”
,
i
);
}
for
(
long
double
i
=
0
;
i
<
1
;
i
+=
0。01
)
{
printf
(
“%Lf
\r\n
”
,
i
);
}
}
注意其中輸出
到0。830000之後明顯出現了誤差
3、反向驗證第二點的儲存推斷
上面的計算,我們可以透過一個小程式碼反向驗證,程式碼如下
#include
int
main
()
{
/*0b1000000010010010000111111010000*/
float
num
=
3。14159f
;
int
*
p
=
(
int
*
)
&
num
;
printf
(
“0x%x
\n
”
,
*
p
);
return
0
;
}
輸出16進位制資料
so~~~~
3。14159
≈
0x40490fd0
=
0 10000000 10010010000111111010000
16進位制對應二進位制資料
對於double 和 long double的大小和精度可以透過這個方式來驗證。
4、浮點型printf輸出格式
printf輸出範圍 %f %g %Lf %e
#include
void
main
(
void
)
{
float
f_sum
=
0
;
double
d_test
=
0
;
f_sum
=
-
3。4
*
10e-38
;
d_test
=
-
1。7
*
10e-308
;
printf
(
“%。38f
\r\n
”
,
f_sum
);
printf
(
“%。308f
\r\n
”
,
d_test
);
printf
(
“%g
\r\n
”
,
f_sum
);
printf
(
“%g
\r\n
”
,
d_test
);
f_sum
=
3。4
*
10e37
;
d_test
=
1。7
*
10e307
;
printf
(
“%g
\r\n
”
,
f_sum
);
printf
(
“%g
\r\n
”
,
d_test
);
}
輸出如下
weiqifa@ubuntu:~/c/float$ gcc float。c && a。out
-0。00000000000000000000000000000000000034
-0。00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017
-3。4e-37
-1。7e-307
3。4e+38
1。7e+308
weiqifa@ubuntu:~/c/float$
5、 精度問題
浮點數在記憶體中是按科學計數法來儲存的,其整數部分始終是一個隱含著的“1”,由於它是不變的,故不能對精度造成影響。
float:2^23 = 8388608,一共七位,這意味著最多能有7位有效數字,但絕對能保證的為6位,也即float的精度為6~7位有效數字;
double:2^52 = 4503599627370496,一共16位,同理,double的精度為15~16位。
小程式碼舉例
#include
“stdio。h”
int
main
(
void
)
{
float
fa
=
1。0f
;
float
fb
=
-
2。123456789f
;
float
fc
=
3。999999f
;
double
da
=
1。0
;
double
db
=
-
4。0000000
;
double
dc
=
3。123456789012345
;
printf
(
“%-32。32f
\r\n
%-32。32f
\r\n
%-32。32f
\r\n
”
,
fa
,
fb
,
fc
);
printf
(
“%-64。64f
\r\n
%-64。64f
\r\n
%-64。64f
\r\n
”
,
da
,
db
,
dc
);
return
0
;
}
輸出
1。00000000000000000000000000000000
-2。12345671653747558593750000000000
3。99999904632568359375000000000000
1。0000000000000000000000000000000000000000000000000000000000000000
-4。0000000000000000000000000000000000000000000000000000000000000000
3。1234567890123448030692543397890403866767883300781250000000000000
6、浮點值和 “0”
不知道大家對精度是怎麼看的,理論上浮點是永遠不可能等於
“0”
的,只能無盡接近於
“0”
,所以你拿浮點型 和
“0”
比較 ,千萬千萬不要用 “== ”恆等於符號,而是用大小於符號,精度越大,說明越無盡接近於
“0”
,有時候float的精度容易引起問題,看下面的例子。——-之前寫的,下面論證是否正確
評論已經有人說,浮點值肯定可以等於1,這個不再做論證,現在論證浮點值和0值,是不是相等的。
所以,我做了實驗,我的文章不一定保證正確,但是提出的觀點一定要有論證的根據
晚上回來很累,跟楠哥睡了一下,10點的時候,楠哥又起來了,我也想更新下評論的問題,我測試的程式碼如下,裡面有註釋。
#include “stdio。h”
int main()
{
/*0b01000000010010010000111111010000 3。14159的二進位制*/
/*0b00111111100000000000000000000000 1的二進位制*/
//int it = 0b01000000010010010000111111010000;
int it = 0b00111111100000000000000000000000;
float *num = (float*)&it;
float num1;
int *p = (int *)&num1;
printf(“%f\r\n”,*num);/*輸出我們認為是0 的二進位制數值*/
printf(“%f\r\n”,num1);/*未初始化的float 值*/
printf(“0x%x\r\n”,*p);/*列印裡面的內容*/
/*裡面的內容是 0x401980 */
/*轉成 二進位制是 0b 0100 0000 0001 1001 1000 0000*/
/*這樣好看點 0b 0 10000000 001100110000000*/
int it2 = 0x401980;
float *num3 = (float *)&it2;
/*分別用三種方式輸出*/
printf(“%f\r\n”,*num3);
printf(“%-32。32f\r\n”,*num3);
printf(“%d\r\n”,(int)*num3);
return 0;
}
輸出如下圖
7、總結
1、浮點型在記憶體裡面的儲存方式
2、浮點型的精度問題
3、浮點型和0值比較
後續有新的內容會繼續更新
感謝那些給與批評的人,不過,如果,請關注公眾號
嵌入式Linux
或者給
10毛
讚賞 ,記住是
10毛