这是以前收集的资料,忘了出处了。
㈠ 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);