对整数和浮点数存储,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()
相关文章推荐
- Big/Little Endian——字节存储顺序
- 判断计算机字节存储顺序的方法(Big-endian或Little-endian)
- 判断计算机的字节存储顺序--big endian or little endian?
- 字节顺序:大端字节(big-endian)和小端字节(little-endian)
- 字节顺序:高位优先(big-endian)和低位优先(little-endian)
- 字节顺序:高位优先(big-endian)和低位优先(little-endian)
- 字节顺序 Big-Endian&Little-Endian
- 字节顺序:高位优先(big-endian)和低位优先(little-endian)
- 字节顺序Little Endian / Big Endian
- 关于Big-Endian/Little-Endian 字节顺序的简单记录
- 检查系统字节顺序 little endian OR big endian
- 字节顺序:高位优先(big-endian)和低位优先(little-endian)
- 关于字节在内在存放的顺序(Big Endian and Little Endian)
- 判断字节的升序存储还是降序存储(little-endian or big-endian)
- endianness:little-endian and big-endian 字节存储次序探讨
- 大端字节(big-endian)和小端字节(little-endian):到底哪些地方要注意字节顺序
- 字节顺序Little-Endian, Big-Endian
- 字节存储机制:big-edian 和 little-endian
- 字节顺序(big endian & little endian)
- 简述C语言中的存储模式(字节顺序)以及程序检测