1. 浮点数的存储结构

  • 计算机中的所有数据都是以二进制的形式存储。float类型是32位(4字节),其中:

  • 符号位(1位):0表示正数,1表示负数。

  • 指数位(8位):使用偏移量127(即指数实际值+127)表示。

  • 尾数位(23位):即尾数的二进制表示。

  • 具体存储格式(从高位到低位):

符号位 | 指数位 (8位) | 尾数位 (23位)

2. 浮点数转换过程

  • 无小数的浮点数转换: 例如,浮点数12345.0f的转换过程:

  • 将整数部分转换为二进制。

  • 将小数点向左移动,直到最高位是1,并计算对应的指数。

  • 删除最高位的1(规范化)。

  • 处理尾数,补充为23位。

  • 计算最终的指数,并加上偏移量127。

  • 带小数的浮点数转换: 例如,123.456f的转换:

  • 整数部分直接转换为二进制。

  • 小数部分需要按二进制方法逐位求和,例如0.57826转为二进制小数。

  • 如果尾数超过23位,进行截断,导致精度丢失。

3. 浮点数精度问题

  • 精度丢失:由于浮点数的尾数长度限制(最多23位),某些数(如0.45)无法精确表示。例如,0.45转为二进制时,精度丢失无法完全表示,导致浮点数运算时的精度问题。

  • 二进制与十进制转换误差:像0.45这样的十进制小数转换成二进制时,必须截断尾数或近似表示,导致精度误差。

4. 小数的二进制表示

  • 二进制小数(例如0.100101011)的位阶依次是1/(2^1), 1/(2^2), 1/(2^3)等。

  • 例如,0.456无法准确转化为二进制数,导致精度丢失。

5. 常见的浮点数表示实例

  • 例如,123.456f的最终二进制表示为:

符号位:0
指数位:10000101
尾数位:11101101110100101111001

转换后的16进制表示为42 F6 E9 79,反转后为79 E9 F6 42

  • 对于0.0f,其二进制表示为00 00 00 00

6. C++代码解析浮点数结构

  • 文章最后给出了一个C++函数DecodeFloat,可以分析并输出浮点数的结构。该函数将浮点数的二进制、符号、指数和尾数进行解析,并计算最终的浮点数值。

代码示例:

void DecodeFloat( BYTE pByte[4] )
{
  printf( "原始(十进制):%d  %d  %d  %d\n" , (int)pByte[0],
   (int)pByte[1], (int)pByte[2], (int)pByte[3] );
  printf( "翻转(十进制):%d  %d  %d  %d\n" , (int)pByte[3],
   (int)pByte[2], (int)pByte[1], (int)pByte[0] );
  bitset<32> bitAll( *(ULONG*)pByte );
  string strBinary = bitAll.to_string<char, char_traits<char>, allocator<char> >();
  strBinary.insert( 9, "  " );
  strBinary.insert( 1, "  " );
  cout << "二进制:" << strBinary.c_str() << endl;
  cout << "符号:" << ( bitAll[31] ? "-" : "+" ) << endl;
  bitset<32> bitTemp;
  bitTemp = bitAll;
  bitTemp <<= 1;
  LONG ulExponent = 0;
  for ( int i = 0; i < 8; i++ )
  {
   ulExponent |= ( bitTemp[ 31 - i ] << ( 7 - i ) );
  }
  ulExponent -= 127;
  cout << "指数(十进制):" << ulExponent << endl;
  bitTemp = bitAll;
  bitTemp <<= 9;
  float fMantissa = 1.0f;
  for ( int i = 0; i < 23; i++ )
  {
   bool b = bitTemp[ 31 - i ];
   fMantissa += ( (float)bitTemp[ 31 - i ] / (float)( 2 << i ) );
  }
  cout << "尾数(十进制):"  << fMantissa << endl;
  float fPow;
  if ( ulExponent >= 0 )
  {
   fPow = (float)( 2 << ( ulExponent - 1 ) );
  }
  else
  {
   fPow = 1.0f / (float)( 2 << ( -1 - ulExponent ) );
  }
  cout << "运算结果:" << fMantissa * fPow << endl;
}