您的位置:首页 > 其它

对整数和浮点数存储,little-endian和big-endian字节顺序,以及位运算的一点回顾

2012-03-04 15:43 489 查看

对问题的一些理解

1.位运算及其相关运算

位运算 &,|,^,~,<<,>>,+,!

用 异或^ 可以交换两个变量,不需要中间变量

a = a ^ b;   // a = 0000 1111

b = b ^ a;   // b = 0000 0111 = 7

a = a ^ b; // a = 0000 1000 = 8

明白其中的道理了吗?其中还有个加减法的版本:

a =a + b;

b =a - b;

a =a - b;

利用这个性质,我们可以求一个(都是无符号整数)整数中有多少位为1。

x= 6;

count= 0;

while( x )

{

x &= ( x - 1 );

++count;

}

x&(x-1) 可以消除最后面的一个1

<< 算数左移 >> 算数右移 逻辑左移,逻辑右移

C语言给你的移动运算包含有符号整数,左移和逻辑左移相同,右移则不一样。算数右移根据最高位符号位填补,而逻辑右移则直接补0.

比如一个有符号位的8位二进制数11001101,逻辑右移就不管符号位,如果移一位就变成01100110。算术右移要管符号位,右移一位变成10100110。

逻辑左移=算数左移,右边统一添0

逻辑右移,左边统一添0

算数右移,左边添加的数和符号有关

e.g:1010101010,其中[]位是添加的数字

逻辑左移一位:010101010[0]

算数左移一位:010101010[0]

逻辑右移一位:[0]101010101

算数右移一位:[1]101010101

/*exchange two integervalues*/

void swap(int * a, int * b)

{

*a ^= *b;

*b ^= *a;

*a ^= *b;

}

/*the max length ofunsigned int*/

int int_size ()

{

unsigned int bits;

int size = 0;while ( bits ) {

++size;

bits >>= 1;

}

return size;

}

/*a bit mover for unsignedint

if n > 0 move left for n bits,else move right*/

unsigned int bit_shift (unsigned int value,int n)

{

int intsize=int_size(); /*the length of unsigned int*/if(n>0 && n<intsize) /*move left*/

value<<=n;

else if (n<0 && n> -intsize) /*move right*/

value>>=-n;

else

value=0;

return value;

}

2浮点数和整数在内存中的存储

2.1问题描述

#include <stdio.h>

  voidmain(void){

  int num=9; /* num是整型变量,设为9 */

  float* pFloat=# /* pFloat表示num的内存地址,但是设为浮点数 */

  printf("num的值为:%d ",num);/* 显示num的整型值 */

  printf("*pFloat的值为:%f",*pFloat); /* 显示num的浮点值*/

  *pFloat=9.0; /* 将num的值改为浮点数 */

  printf("num的值为:%d",num); /* 显示num的整型值 */

  printf("*pFloat的值为:%f",*pFloat); /* 显示num的浮点值*/

  }

  运行结果如下:

  num的值为:9

  *pFloat的值为:0.000000

  num的值为:1091567616

  *pFloat的值为:9.000000

  我很惊讶,num和*pFloat在内存中明明是同一个数,为什么浮点数和整数的解读结果会差别这么大?

  要理解这个结果,一定要搞懂浮点数在计算机内部的表示方法。我读了一些资料,下面就是我的笔记。

2.2 整数和浮点数的内部表示形式

  在讨论浮点数之前,先看一下整数在计算机内部是怎样表示的。

  int num=9;

  上面这条命令,声明了一个整数变量,类型为int,值为9(二进制写法为1001)。普通的32位计算机,用4个字节表示int变量,所以9就被保存为0000000000000000 00000000 00001001,写成16进制就是0x00000009。

  那么,我们的问题就简化成:为什么0x00000009还原成浮点数,就成了0.000000?

  根据国际标准IEEE 754,任意一个二进制浮点数V可以表示成下面的形式:

  V = (-1)^s×M×2^E

  (1)(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。

  (2)M表示有效数字,大于等于1,小于2。

  (3)2^E表示指数位。

  举例来说,十进制的5.0,写成二进制是101.0,相当于1.01×2^2。那么,按照上面V的格式,可以得出s=0,M=1.01,E=2。

  十进制的-5.0,写成二进制是-101.0,相当于-1.01×2^2。那么,s=1,M=1.01,E=2。

  IEEE 754规定,对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。



  对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。

2.3IEEE 754浮点数分析

  IEEE 754对有效数字M和指数E,还有一些特别规定。

  前面说过,1≤M<2,也就是说,M可以写成1.xxxxxx的形式,其中xxxxxx表示小数部分。IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。

  至于指数E,情况就比较复杂。

  首先,E为一个无符号整数(unsigned int)。这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,E的真实值必须再减去一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。

  比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

  然后,指数E还可以再分成三种情况:

  (1)E不全为0或不全为1。这时,浮点数就采用上面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。

  (2)E全为0。这时,浮点数的指数E等于1-127(或者1-1023),有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。

  (3)E全为1。这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);如果有效数字M不全为0,表示这个数不是一个数(NaN)。

  6.

  好了,关于浮点数的表示规则,就说到这里。

  下面,让我们回到一开始的问题:为什么0x00000009还原成浮点数,就成了0.000000?

  首先,将0x00000009拆分,得到第一位符号位s=0,后面8位的指数E=00000000,最后23位的有效数字M=000 0000 0000 0000 0000 1001。

  由于指数E全为0,所以符合上一节的第二种情况。因此,浮点数V就写成:

  V=(-1)^0×0.00000000000000000001001×2^(-126)=1.001×2^(-146)

  显然,V是一个很小的接近于0的正数,所以用十进制小数表示就是0.000000。

  7.

  再看例题的第二部分。

  请问浮点数9.0,如何用二进制表示?还原成十进制又是多少?

  首先,浮点数9.0等于二进制的1001.0,即1.001×2^3。

  那么,第一位的符号位s=0,有效数字M等于001后面再加20个0,凑满23位,指数E等于3+127=130,即10000010。

  所以,写成二进制形式,应该是s+E+M,即010000010 001 0000 0000 0000 0000 0000。这个32位的二进制数,还原成十进制,正是1091567616。

3.Big-endian 和 little-endian

3.1基本概念

首先,明确几个概念
1、字节:固定为8位二进制
2、字长:字为计算机一次能处理的数据长度
8位机中为8位二进制 即1个字节
16位机中为16位二进制 即2个字节
32位机中为32位二进制 即4个字节
3、char类型长度:固定为一个字节,即8位二进制
4、int类型长度:固定为一个字长
实例如下:环境为32位机
int i=1;
Big-endian方式下: 0x00 0x00 0x00 0x01
Little-endian方式下:0x01 0x00 0x00 0x00
地址: 1000 1001 1002 1003
获取i的地址,为1000处 &i
强制转换指针类型为char * ,即从地址起点截断1个字节 (char*)&i
此时
Big-endian方式下为:0x00
Little-endian方式下为:0x01
得出如下结论
int i=1;
若*(char*)&i等于1 则为Little-endian方式,否则为Big-endian方式

3.2 应用机型

采用Little-Endian的

操作系统FreeBSD,Linux,Windows

x86的机器

采Big-Endian的

操作系统 MACOS

ARM, Alpha,摩托罗拉的PowerPC

Network中的变量

Java语言.

3.3函数验证

【用函数判断系统是Big Endian还是Little Endian】

bool IsBig_Endian()

//如果字节序为big-endian,返回true;

//反之为 little-endian,返回false

{

unsigned short test = 0x1122;

if(*( (unsigned char*) &test ) == 0x11)

return TRUE;

else

return FALSE;
}//IsBig_Endian()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: