float 和 double (我記憶體裡的浮點型資料?)

前面隨便說說~~~~~

二更,因為好幾個人因為這篇文章把我批鬥了,把有問題的地方修正。

今天看到一個問題

能不能用double去取代float?

前段時間,有個朋友問我,在java裡面我想把一個高精度的

浮點

型儲存下來,但是每次儲存的時候都會被強制降低精度,對於浮點型的理解,真的非常非常重要,特別對嵌入式軟體開發,或者演算法開發,涉及到資料類的,浮點型非常非常關鍵,就比如,微信為什麼不讓我發0。0000000001 元的

微信紅包

呢? 有沒有想過這個問題?如果這樣做對於他們伺服器後臺的運算能力要求非常高,so,~~~~~

所以想簡單寫一下,float和double的區別以及應用場景

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 和 double (我記憶體裡的浮點型資料?)

我拿

float

來舉個栗子:

float 和 double (我記憶體裡的浮點型資料?)

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

舉例:

float 和 double (我記憶體裡的浮點型資料?)

剩餘的位元用於有效數字。每個位表示從左側算起的

2的負冪,float一共23位

,舉例:

float 和 double (我記憶體裡的浮點型資料?)

某些平臺假定有效數中的“隱藏”前導位始終設定為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這樣的值不能用有限數量的十進位制數字表示,由於值是近似值,因此使用它們進行計算也是近似值,並且累積誤差會累積。

float 和 double (我記憶體裡的浮點型資料?)

#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

);

}

}

注意其中輸出

float 和 double (我記憶體裡的浮點型資料?)

到0。830000之後明顯出現了誤差

float 和 double (我記憶體裡的浮點型資料?)

3、反向驗證第二點的儲存推斷

上面的計算,我們可以透過一個小程式碼反向驗證,程式碼如下

#include

int

main

()

{

/*0b1000000010010010000111111010000*/

float

num

=

3。14159f

int

*

p

=

int

*

&

num

printf

“0x%x

\n

*

p

);

return

0

}

float 和 double (我記憶體裡的浮點型資料?)

輸出16進位制資料

so~~~~

3。14159

0x40490fd0

=

‭0 10000000 10010010000111111010000‬

float 和 double (我記憶體裡的浮點型資料?)

16進位制對應二進位制資料

對於double 和 long double的大小和精度可以透過這個方式來驗證。

float 和 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$

float 和 double (我記憶體裡的浮點型資料?)

5、 精度問題

浮點數在記憶體中是按科學計數法來儲存的,其整數部分始終是一個隱含著的“1”,由於它是不變的,故不能對精度造成影響。

float:2^23 = 8388608,一共七位,這意味著最多能有7位有效數字,但絕對能保證的為6位,也即float的精度為6~7位有效數字;

double:2^52 = 4503599627370496,一共16位,同理,double的精度為15~16位。

float 和 double (我記憶體裡的浮點型資料?)

小程式碼舉例

#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

float 和 double (我記憶體裡的浮點型資料?)

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;

}

輸出如下圖

float 和 double (我記憶體裡的浮點型資料?)

float 和 double (我記憶體裡的浮點型資料?)

7、總結

1、浮點型在記憶體裡面的儲存方式

2、浮點型的精度問題

3、浮點型和0值比較

後續有新的內容會繼續更新

感謝那些給與批評的人,不過,如果,請關注公眾號

嵌入式Linux

或者給

10毛

讚賞 ,記住是

10毛