這是以前收集的資料,忘了出處了。
㈠ float格式簡談
Inter 80386/80387 及以上型號CPU有三種浮點類型,即短實數、長實數和80位臨時實數,分別佔用4字節、8字節和10字節,對應着C/C++中的float、double和long double[注1],我以 Real4、Real8和Real0表示之。
每種浮點格式皆應符合IEEE標準,稱爲規格化數,不符合IEEE標準的浮點格式稱爲非格式化數(NAN),我以最簡單的float格式舉例。
Float格式數據長32 bits,最高位爲符號位:0爲正,1爲負;緊接着的8位爲階碼:爲了便於比較大小,其固定偏移7FH長,即0實際表示-7FH,7FH實際表示0,0FFH實際表示80H;餘下的低23位爲尾數(有效數字),爲了使有效數字達到最大精度,這23個有效數字隱含着固定位1[注2],比如尾數10000000000000000000001其實就是1.10000000000000000000001,1被省略,而小數點固定在首位。
根據以上規則可以知道float所能表示的絕對值大小範圍是
0 000,0000,0 000,0000,0000,0000,0000,0000 B 至
0 111,1111,1 111,1111,1111,1111,1111,1111 B
即±1.00000000000000000000000B×2(0x00-0x7F)到±1.11111111111111111111111B×2(0xFF-0x7F)
然而事實上並不是這樣,因爲從這個範圍可以看出它並不能表示0.0,而0.0爲常用數字,所以特別規定±1.00000000000000000000000B×200爲零,規定指數爲0xFF的數字爲非法數字,因此float實際的絕對值範圍(除去0以外)是
0 000,0000,0 000,0000,0000,0000,0000,0001 B 至
0 111,1111,0 111,1111,1111,1111,1111,1111 B
即 0 並上 ±1.00000000000000000000001B×2(0x00-0x7F)到±1.11111111111111111111111B×2(0xFE-0x7F)
可能的格式狀態:
+不支持
+非有效數
-不支持
-非有效數
+規格化
+∞
-規格化
-∞
+0
+空
-0
-空
+不能規格化
-不能規格化
可能的異常:
無效操作
上溢
下溢
除零
不可規格化操作數
精度不足
㈡ 注意事項
a. 可以看出0可以用+0表示,也可以用-0表示,爲此,CPU在比較零值時作了特殊處理,結果是±0雖然在存儲器上格式不同,但比較值相同,然而如果是用字節來比較float類型大小時卻需要注意這一點。
b. 運算上溢的值不是實際值,而是特值 0 111,1111,1 000,0000,0000,0000,0000,0000 B,例如 1.0×2127 + 1.5×2127不等於 1.5×2128,雖然它有能力表示1.5×2128。
c.
㈢ 在C++中的部分解決方案及遺留問題
5.參考資料
《Inter 80X86/80387 彙編指南》
6.附錄
[注1]:M$認爲double精度已足夠,故在MVC++5.0及以後取消80位了臨時實數,令long double等同於double,但在本文中的long double還是指80位臨時實數。
[注2]:各種浮點類型格式類似,double指數偏移基數3FFH,long double指數偏移基數3FFFH,但long double特殊在無有效數字隱含位。
real04:符號位1,階碼08(固定偏移 7F),尾數23,固定隱含位有;
real08:符號位1,階碼11(固定偏移 3FF),尾數52,固定隱含位有;
real10:符號位1,階碼15(固定偏移3FFF),尾數64,固定隱含位無;
以下是我用VC6編的一段從文件中讀取寫入浮點數的代碼。
// 將浮點數寫入文件
void CTestFloatDlg::WriteToFile(const CString & fileName)
{
UpdateData();
CFile dataFile;
CFileException e;
try
{
if(!dataFile.Open(fileName, CFile::modeWrite | CFile::modeCreate | CFile::shareExclusive, &e))
{
DWORD ERR = ::GetLastError();
return;
}
dataFile.SeekToBegin();
dataFile.Write(&m_fEdit1, MAX_READ);
dataFile.Close();
}
catch(CFileException* pEx )
{
pEx->Delete();
}
}
// 從文件中讀取浮點數
void CTestFloatDlg::ReadFromFile(const CString & fileName)
{
unsigned char byteArrB[8];
byteArrB[0] = 1;
byteArrB[1] = 2;
byteArrB[2] = 4;
byteArrB[3] = 0X8;
byteArrB[4] = 0X10;
byteArrB[5] = 0X20;
byteArrB[6] = 0X40;
byteArrB[7] = 0X80;
CFile dataFile;
CFileException e;
try
{
if(!dataFile.Open(fileName, CFile::modeRead | CFile::shareExclusive, &e))
{
DWORD ERR = ::GetLastError();
return;
}
dataFile.SeekToBegin();
char data[MAX_READ];
memset(data, 0, sizeof(char) * MAX_READ);
dataFile.Read(data, MAX_READ);
dataFile.Close();
// 取出符號位,判斷正負號
int iSign = data[3] >> 7 > 0 ? -1 : 1;
if ( -1 == iSign )
{
data[3] &= 0x7F;
}
// 計算 8位階碼
int iExp = data[3] << 1;
int iTemp = (data[2] >> 7) & 0X1;
// float型固定偏移爲0x7F, 所以需要減去該數
iExp = iExp + iTemp - 0x7F;
// 若指數在有效範圍內
if (iExp > -127 && iExp < 128)
{
// 計算隱含位
m_fEdit2 = pow(2, iExp);
double dTemp = 0;
// 取低7位
data[2] &= 0x7F;
for (int i = 6; i >= 0; i--)
{
iExp--;
int iTemp = data[2] & byteArrB[i];
iTemp >>= i;
m_fEdit2 += iTemp * pow(2, iExp);
}
for (i = 1; i >= 0; i--)
{
for (int j = 7; j >= 0; j--)
{
iExp--;
int iTemp = data[i] & byteArrB[j];
iTemp >>= j;
m_fEdit2 += iTemp * pow(2, iExp);
}
}
}
else
{
m_fEdit2 = 0;
}
UpdateData(FALSE);
}
catch(CFileException* pEx )
{
pEx->Delete();
}
}
// 讀寫的例子
CString strFileName = "C://Read.txt";
WriteToFile(strFileName);
CString strFileName = "C://Read.txt";
ReadFromFile(strFileName);