新聞中心
二更,因?yàn)楹脦讉€(gè)人因?yàn)檫@篇文章把我批斗了,把有問(wèn)題的地方修正。
公司主營(yíng)業(yè)務(wù):成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。創(chuàng)新互聯(lián)推出焉耆免費(fèi)做網(wǎng)站回饋大家。
今天看到一個(gè)問(wèn)題
能不能用 double 去取代 float ?
前段時(shí)間,有個(gè)朋友問(wèn)我,在 java 里面我想把一個(gè)高精度的浮點(diǎn)型存儲(chǔ)下來(lái),但是每次存儲(chǔ)的時(shí)候都會(huì)被強(qiáng)制降低精度,對(duì)于浮點(diǎn)型的理解,真的非常非常重要,特別對(duì)嵌入式軟件開(kāi)發(fā),或者算法開(kāi)發(fā),涉及到數(shù)據(jù)類的,浮點(diǎn)型非常非常關(guān)鍵,就比如,微信為什么不讓我發(fā) 0.0000000001 元的微信紅包呢? 有沒(méi)有想過(guò)這個(gè)問(wèn)題?如果這樣做對(duì)于他們服務(wù)器后臺(tái)的運(yùn)算能力要求非常高, so , ~~~~~
所以想簡(jiǎn)單寫(xiě)一下, float 和 double 的區(qū)別以及應(yīng)用場(chǎng)景
1 、浮點(diǎn)型的值范圍
float 和 double 的范圍是由指數(shù)的位數(shù)來(lái)決定的。在 VC++6.0 中, float 占 4 個(gè)字節(jié), double 占 8 個(gè)字節(jié)。
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 、浮點(diǎn)型在內(nèi)存里面是如何存儲(chǔ)的?
我相信這個(gè)問(wèn)題大家沒(méi)有好好的去考慮過(guò),浮點(diǎn)型如何存儲(chǔ),這才是我們討論浮點(diǎn)型的關(guān)鍵所在,關(guān)于上面的浮點(diǎn)型取值范圍,也是網(wǎng)上拷貝下來(lái)的,至于真實(shí)性,我覺(jué)得你們要看了這部分才可能真正理解浮點(diǎn)型,而且最好在自己的編譯器去測(cè)試,浮點(diǎn)型是可以等于負(fù)數(shù)的,所以上面的范圍,你們認(rèn)為是正確的嗎?
我拿 float 來(lái)舉個(gè)栗子:
float 在內(nèi)存中的存儲(chǔ)方式
以下 部分如發(fā)現(xiàn)問(wèn)題,請(qǐng)留言,會(huì)發(fā)小小紅包感謝,微信 weiqifa0
首先使用基數(shù) 2 而不是基數(shù) 10 來(lái)表示科學(xué)計(jì)數(shù)法變體中的浮點(diǎn)值。例如,值 3.14159 可以表示為
1.570795 * 2^{1}
1.570795 是 有效數(shù)字又名尾數(shù)(在上圖中指尾數(shù)部分) ; 它是包含有效數(shù)字的數(shù)字的一部分。此值乘以基數(shù) 2 ,上升到 1 的冪,得到 3.14159 。
浮點(diǎn)數(shù)通過(guò)存儲(chǔ) 有效數(shù)和指數(shù)(以及符號(hào)位) 進(jìn)行編碼。
典型的 32 位布局如下所示:
3 32222222 22211111111110000000000
1 09876543 21098765432109876543210
+-+--------+-----------------------+
| | | |
+-+--------+-----------------------+
^ ^ ^
| | |
| | +-- 有效數(shù)
| |
| +------------------- 指數(shù)
|
+------------------------ 符號(hào)位
與有符號(hào)整數(shù)類型一樣,高位表示符號(hào) ; 表示 正 值, 1 表示 負(fù) 值。
而對(duì)于指數(shù)部分,因?yàn)橹笖?shù)可正可負(fù), 8 位的指數(shù)位能表示的指數(shù)范圍就應(yīng)該為 :-127-128 了, 所以指數(shù)部分的存儲(chǔ)采用移位存儲(chǔ), 存儲(chǔ)的數(shù)據(jù)為元數(shù)據(jù) +127 。
舉例:
剩余的比特用于有效數(shù)字。每個(gè)位表示從左側(cè)算起的 2 的負(fù)冪, float 一共 23 位 ,舉例:
某些平臺(tái)假定有效數(shù)中的 “ 隱藏 ” 前導(dǎo)位始終設(shè)置為 1 ,因此有效數(shù)中的值始終在 [0.5,1 之間 ] 。這允許這些平臺(tái)以稍高的精度存儲(chǔ)值。
所以 3.14159 的值將表示為
0 10000000 10010010000111111010000
^ ^ ^
| | |
| | + --- 有效數(shù) = 1.570795 ...
| |
| + ------------------- 指數(shù) = 2 ( 130 - 128 )
|
+ ------------------------- sign = 0 (正面)
value = 1 (符號(hào)位) * 2 (指數(shù)位) * (有效數(shù)字)
值 = 1 0 * 2^1 * 1.570795 ...
值 = 3.14159 ......
現(xiàn)在,如果你將有效數(shù)字中的所有位相加,你會(huì)注意到它們總共 不是 3.14195 ;
他們實(shí)際上 是 3.141590118408203125 ,(小編實(shí)測(cè)數(shù)據(jù)) 。
沒(méi)有足夠的位來(lái)準(zhǔn)確存儲(chǔ)值 ; 我們只能存儲(chǔ)一個(gè)近似值。有效數(shù)字中的位數(shù)決定了精度 23 位給出了大約 6 位精度的十進(jìn)制數(shù)字 。 64 位浮點(diǎn)類型在有效位數(shù)中提供足夠的位, 可提供大約 12 到 15 位的精度 。但要注意,有一些數(shù)值不能被精確表示。就像 1/3 這樣的值不能用有限數(shù)量的十進(jìn)制數(shù)字表示,由于值是近似值,因此使用它們進(jìn)行計(jì)算也是近似值,并且累積誤差會(huì)累積。
#include
void main ( void )
{
for ( float i = ; i < 1 ;i += 0.01 )
{
printf( "%f \r\n" ,i);
}
for ( double i = ; i < 1 ;i += 0.01 )
{
printf( "%f\r\n" ,i);
}
for ( long double i = ; i < 1 ;i += 0.01 )
{
printf( "%Lf\r\n" ,i);
}
}
注意其中輸出
到 0.830000 之后明顯出現(xiàn)了誤差
3 、反向驗(yàn)證第二點(diǎn)的存儲(chǔ)推斷
上面的計(jì)算,我們可以通過(guò)一個(gè)小代碼反向驗(yàn)證,代碼如下
#include
int main ()
{
/*0b1000000010010010000111111010000*/
float num = 3.14159f ;
int * p = ( int * ) & num;
printf( "0x%x\n" , * p);
return ;
}
輸出 16 進(jìn)制數(shù)據(jù)
so~~~~
3.14159 ≈ 0x40490fd0 = 0 10000000 10010010000111111010000
16 進(jìn)制對(duì)應(yīng)二進(jìn)制數(shù)據(jù)
對(duì)于 double 和 long double 的大小和精度可以通過(guò)這個(gè)方式來(lái)驗(yàn)證。
4 、浮點(diǎn)型 printf 輸出格式
printf 輸出范圍 %f %g %Lf %e
#include
void main ( void )
{
float f_sum = ;
double d_test = ;
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 、 精度問(wèn)題
浮點(diǎn)數(shù)在內(nèi)存中是按科學(xué)計(jì)數(shù)法來(lái)存儲(chǔ)的,其整數(shù)部分始終是一個(gè)隱含著的 "1" ,由于它是不變的,故不能對(duì)精度造成影響。
float : 2^23 = 8388608 ,一共七位,這意味著最多能有 7 位有效數(shù)字,但絕對(duì)能保證的為 6 位,也即 float 的精度為 6~7 位有效數(shù)字;
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 ;
}
輸出
1.00000000000000000000000000000000
-2.12345671653747558593750000000000
3.99999904632568359375000000000000
1.0000000000000000000000000000000000000000000000000000000000000000
-4.0000000000000000000000000000000000000000000000000000000000000000
3.1234567890123448030692543397890403866767883300781250000000000000
6 、浮點(diǎn)值和 “0”
不知道大家對(duì)精度是怎么看的,理論上浮點(diǎn)是永遠(yuǎn)不可能等于 “0” 的,只能無(wú)盡接近于 “0” ,所以你拿浮點(diǎn)型 和 “0” 比較 ,千萬(wàn)千萬(wàn)不要用 “== ” 恒等于符號(hào),而是用大小于符號(hào),精度越大,說(shuō)明越無(wú)盡接近于 “0” ,有時(shí)候 float 的精度容易引起問(wèn)題,看下面的例子。 --- 之前寫(xiě)的,下面論證是否正確
評(píng)論已經(jīng)有人說(shuō),浮點(diǎn)值肯定可以等于 1 ,這個(gè)不再做論證,現(xiàn)在論證浮點(diǎn)值和 值,是不是相等的。
所以,我做了實(shí)驗(yàn),我的文章不一定保證正確,但是提出的觀點(diǎn)一定要有論證的根據(jù)
晚上回來(lái)很累,跟楠哥睡了一下, 10 點(diǎn)的時(shí)候,楠哥又起來(lái)了,我也想更新下評(píng)論的問(wèn)題,我測(cè)試的代碼如下,里面有注釋。
#include "stdio.h"
int main()
{
/*0b01000000010010010000111111010000 3.14159 的二進(jìn)制 */
/*0b00111111100000000000000000000000 1 的二進(jìn)制 */
//int it = 0b01000000010010010000111111010000;
int it = 0b00111111100000000000000000000000;
float *num = (float*)⁢
float num1;
int *p = (int *)&num1;
printf("%f\r\n",*num);/* 輸出我們認(rèn)為是 0 的二進(jìn)制數(shù)值 */
printf("%f\r\n",num1);/* 未初始化的 float 值 */
printf("0x%x\r\n",*p);/* 打印里面的內(nèi)容 */
/* 里面的內(nèi)容是 0x401980 */
/* 轉(zhuǎn)成 二進(jìn)制是 0b 0100 0000 0001 1001 1000 0000*/
/* 這樣好看點(diǎn) 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 、總結(jié)
1 、浮點(diǎn)型在內(nèi)存里面的存儲(chǔ)方式
2 、浮點(diǎn)型的精度問(wèn)題
3 、浮點(diǎn)型的
本文標(biāo)題:float和double(我內(nèi)存里的浮點(diǎn)型數(shù)據(jù)?)
轉(zhuǎn)載注明:http://www.ef60e0e.cn/article/ppohic.html