一個哥們在qq群里問了一個關於浮點數的程序,然后行了行浮點數的知識.竟然忘了,所有找了些文章.回憶回憶,理解理解
首先來聊天他的問題和讓我無言以對的解決辦法吧
""十六進制轉負數浮點數怎么轉換啊"
然后我默默的寫了一個下面的東東
#include "stdafx.h"
#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
using namespace std;
float getFloat()
{
return (float)3.1423;
}
string FloatToHex ( float fNum )
{
int nInteger = (int)(fNum);
int nPower = 0;
while( fNum - nInteger > 1e-5 )
{
fNum *= 10;
nInteger = (int)(fNum);
nPower++;
}
std::cout<<nPower<<std::endl;
return "";
}
string FloatLength ( float fNum )
{
char cAryFloat[100] = {0};
sprint(cAryFloat, "%g", fNum);//從字符串cAryFloat中統計小數點后面有幾位!!! %f 3.142300 %g 3.1423 %e 31423 + 1e-4
return "";
}
int _tmain(int argc, _TCHAR* argv[]) { FloatToHex(getFloat()); getchar(); return 0; } /* 在getFloat()函數中如果 return (float)3.14 那么最后打印的是2 在getFloat()函數中如果 return (float)3.1423 那么最后打印的是7 糾結了半天,猜測着可能你是返回3.1423但是你沒辦法控制float的精度,所有最后會打印7吧.
float最后可能是31422.9999999 打印的時候打印出了31423.但是強轉int的話int仍然是32422.
所以又寫了另外的函數string FloatLength ( float fNum ) 這個統計起來就沒問題了..
*/
下面的是他的代碼:
int i = 0xbd600d1b;
float *f = (float *)&i;
printf("%f\n",*f);
float ff = -0.0547;
int *ii = (int *)&ff;
printf("%X\n",*ii);
反正也對吧...但是總有一種無言以對的感覺...
接下來就要進入正題,講一講浮點數在內存中的表示了.
首先是在c和c#中的浮點類型分兩種 單精度float和雙精度double. float或者double在內存中的組織都遵循IEEE的規范的,float遵從的是IEEE R32.24 ,而double 遵從的是R64.53
然后呢對於一個存儲在內存的浮點數都包括3部分 : 符號位 指數位 小數部分 (而float和double的區別就是各部分占得位數可能不同)

好了,既然知道了浮點數在內存中的表示方法,那么來個有意思的東西8.25 用10進制表示是82.5 * 10e-1, 那么用2進制表示是多少呢?
剛開始的時候我真的蒙了,真心不知道怎么用二進制表示啊(好吧8我是知道怎么用二進制表示的,只要大於0的數我都能用二進制表示出來,但是小數就呵呵了,從來沒用到過) 答案是1000.01 琢磨了十分鍾才搞明白怎么解這個東西:
8--->1000 0.25-->0[0*20] . 0 [0*2-1] 1[1*2-2] 所以就是1000.01了 再轉換一下100.001 * 21 在轉換就成了 1.00001 * 23
在二進制中010 跟10是等價的.所以啊最后如果用科學計數法表示的話都可以表示成1.xxx * 2xxx的形式 (注意跟十進制的區別80 你可以表示成8*101 但是二進制中不可能出現8這個數字,它只有0和1)
那么好啊,既然所有的二進制都可以表示成1.xxx * 2xxx那么小數點前面的肯定都是1了所以在存儲到內存的時候默認都是1.xxx就好了,也沒必要在內存中存儲1這個bit了所以23bit的尾數部分,可以表示的精度卻變成了24bit,道理就是在這里,那24bit能精確到小數點后幾位呢,我們知道9的二進制表示為1001,所以4bit能精確十進制中的1位小數點(意思是要表示0-9必須最少要用4bit.),24bit就能使float能精確到小數點后6位(24/4=6),而對於指數部分,因為指數可正可負,8位的指數位能表示的指數范圍就應該為:-127-128了,所以指數部分的存儲采用移位存儲,存儲的數據為元數據+127,下面就看看8.25和120.5在內存中真正的存儲方式。
好吧關於為什么指數要+127的問題,我也不知道.從網上找了段講解粘貼在下方了,有機會再搞清楚吧:
書上說之所以要將指數加上 127 來得到階碼,是為了簡化浮點數的比較運算,這一點我沒有體會出來。但是通過 127 這個偏移量 (移碼),可以區分出指數的正負。階碼為 127 時表示指數為 0;階碼小於 127 時表示負指數;階碼大於 127 時表示正指數。
那么繼續看8.25和120.5的內存表示方法 8.25 == 1.0001 * 23
恩.以上基本就可以把float和double的東西搞得差不多了..
大牛還弄了個例子來說明把一個float賦值給double的話 double的值未必等於float..我們來看看吧
猜測一下下面的程序的輸出結果是什么:
1 float f = 2.2f; 2 double d = (double)f; 3 Console.WriteLine(d.ToString("0.0000000000000")); 4 f = 2.25f; 5 d = (double)f; 6 Console.WriteLine(d.ToString("0.0000000000000"));
可能輸出的結果讓大家疑惑不解,單精度的2.2轉換為雙精度后,精確到小數點后13位后變為了2.2000000476837,而單精度的2.25轉換為雙精度后,變為了2.2500000000000,為何2.2在轉換后的數值更改了而2.25卻沒有更改呢?很奇怪吧?
首先我們看看2.25的單精度存儲方式,很簡單 0 1000 0001 001 0000 0000 0000 0000 0000,而2.25的雙精度表示為:0 100 0000 0001 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000,這樣2.25在進行強制轉換的時候,數值是不會變的,
ps: 先插入一個怎么把十進制小數轉換成二進制的小數的方法---->
而我們再看看2.2呢,2.2用科學計數法表示應該為:將十進制的小數轉換為二進制的小數的方法為將小數*2,取整數部分,所以0.282=0.4,所以二進制小數第一位為0.4的整數部分0,0.4×2=0.8,第二位為0,0.8*2=1.6,第三位為1,0.6×2 = 1.2,第四位為1,0.2*2=0.4,第五位為0,這樣永遠也不可能乘到=1.0,得到的二進制是一個無限循環的排列 00110011001100110011... ,對於單精度數據來說,尾數只能表示24bit的精度,所以2.2的float存儲為: 2.2 == 10.00110011.... == 1.000110011... * 21 .所以指數是127+1=128

但是這樣存儲方式,換算成十進制的值,卻不會是2.2的,應為十進制在轉換為二進制的時候可能會不准確,如2.2,而double類型的數據也存在同樣的問題,所以在浮點數表示中會產生些許的誤差,在單精度轉換為雙精度的時候,也會存在誤差的問題,對於能夠用二進制表示的十進制數據,如2.25,這個誤差就會不存在,所以會出現上面比較奇怪的輸出結果。
恩,學習完了.附上原作者的鏈接 http://www.cnblogs.com/jillzhang/archive/2007/06/24/793901.html
