Chinaunix首页 | 论坛 | 博客
  • 博客访问: 669713
  • 博文数量: 137
  • 博客积分: 7000
  • 博客等级: 少将
  • 技术积分: 1335
  • 用 户 组: 普通用户
  • 注册时间: 2005-11-23 15:18
文章分类

全部博文(137)

文章存档

2010年(2)

2009年(2)

2008年(2)

2007年(30)

2006年(99)

2005年(2)

我的朋友

分类: C/C++

2009-05-03 11:05:59

    一直在忙,有好一阵子没顾得上写blog了,今天一来还是看到有网友对浮点数的十六进制表示问题存有疑问(http://blog.chinaunix.net/u/11768/showart_137027.html),可能还是因为我没把问题说清楚,这回再通过一个简单的例子解释一下,以后该是不会再写与此问题相关的文章了。
    先看看问题:0.6875的16进制如何表示?
    我们知道0.6875是一个浮点数的十进制表示法,而计算机在存储的时候是不能把这样一个小数往存储器里存的,而是通过把数据转换成二进制的形式存储,从而对应到物理上的高电平、低电平来表示(为什么要这样?问问计算机的先驱——图灵大神吧)。那么我们首先来看看,0.6875这样一个小数如何转成一种我们可以看的懂的二进制表示形式呢?
    对于一个整数怎么转二进制想必大家都已经很清楚了,小数其实也是一样的道理。

20 ---- 1/1  ---- 1

2-1 ---- 1/2  ---- 0.5

2-2 ---- 1/4  ---- 0.25

2-3 ---- 1/8  ---- 0.125

2-4 ---- 1/16 ---- 0.0625

2-5 ---- 1/32 ---- 0.03125

...


0.6875 = 0.5 + 0.125 + 0.0625
由此转换我们可以接受的一种二进制表示是0.1011,可这样的表示我们是看懂了,但计算机却看不懂,计算机是无法把小数点往存储器里存的。怎么办呢?于是大家便按照一个统一的约定来表示浮点数在内存中的存储,目前采用的就是IEEE的浮点数规格化表示。关于这个表示方法可以查资料,本blog里也有过两篇文章提到过这个问题(http://blog.chinaunix.net/u/11768/showart.php?id=100351http://blog.chinaunix.net/u/11768/showart_137027.html)。简言之,对于float类型来说,就是以1位的符号位+8位的指数域位+23位的小数域位凑成的32个bit的二进制数来存储。
    还是按照之前的方法移动小数点,留出一个整数位的1,成了1.011,在这过程中小数点右移了一位。而在之前的例子中都是以左移居多。这样,在公式E = e-Bias中,E=126-127=-1,也就是说指数域e=126,也就是说指数域是01111110,因此0.6875在二进制中的存储就应是:
 0 01111110  01100000000000000000000
|1|<---8--->|<---------23---------->|
亦即
0011 1111 0011 0000 0000 0000 0000 0000
就是十六进制的0x3F300000啦。
说了那么多次了,次次都是空口无凭,还是写个程序来验证一下吧。

/*
 * =====================================================================================
 *
 * Filename: floatStorage.c
 *
 * Description: convert the memory storage of a float number to a hexadecimal string
 *
 * Version: 1.0
 * Created:
 * Revision: none
 * Compiler: gcc -Wall -g -o floatStorage floatStorage.c
 *
 * =====================================================================================
 */

#include <stdio.h>

#define F2HEX_LEN 11
#define TRUE (1==1)
#define FALSE (!TRUE)

int isLittleEndian()
{
  int x = 1;
  if (*(char *)&x == 1) {
    printf("little-endian\n");
    return TRUE;
  }
  else {
    printf("big-endian\n");
    return FALSE;
  }
}

char *float2HexStr(float f, char *buff)
{
  unsigned char *pf = (unsigned char *)&f;

  if (sizeof(f)>sizeof(float))
  return NULL;

  if (isLittleEndian())
    snprintf(buff, F2HEX_LEN, "0x%02x%02x%02x%02x", pf[3], pf[2], pf[1], pf[0]);
  else
    snprintf(buff, F2HEX_LEN, "0x%02x%02x%02x%02x", pf[0], pf[1], pf[2], pf[3]);

  return buff;
}

int main() {
  float f1 = 0.6875, f2 = -0.6875, f3 = 551051722752.0;
  char buff[F2HEX_LEN];

  printf("f1=%g=%s\n\n", f1, float2HexStr(f1, buff));
  printf("f2=%g=%s\n\n", f2, float2HexStr(f2, buff));
  printf("f3=%g=%s\n\n", f3, float2HexStr(f3, buff));

  return 0;
}


运行结果为:

> ./floatStorage
little-endian
f1=0.6875=0x3f300000

little-endian
f2=-0.6875=0xbf300000

little-endian
f3=5.51052e+11=0x53004d3e

    通过这个小sample我们还可以看到符号位的问题,还有之前的文章中讨论的那个浮点数551051722752是不是像我说的那样存储。
阅读(1362) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

tangcong292009-07-20 20:19:48

拜读!!

tangcong292009-07-20 20:19:48

拜读!!