字长:指针占用的内存空间大小。在32位机器上是4字节,在64位机器上是8字节。
小/大端法
移位运算
无符号整数
对于一个位表示 ,其表示的十进制数为:
有符号整数
对于一个位表示 ,其表示的十进制数为:
这种编码方式成为补码。
一些特殊的值
注意,补码的范围是不对称的,即 ,因为一半的位模式是负数,另一半的位模式是0和正数。
转换时确保位模式不会发生变化。
有符号转无符号
推导:
对于相同的位模式 ,我们有:
因此当 为负数时, 为 ,两者相差为 。反之, 为 ,两者相同。
无符号转有符号
总的来说,补码符号位为 时,两者转换前后相同。反之,转化前后相差 。
有符号和无符号同时出现时
在C语言中,有符号和无符号同时出现时,会隐式地将有符号转为无符号。
扩展
零扩展和最高位为 时的符号扩展,扩展前后表示的十进制整数一定是不变的。
最高位为 时的符号扩展,对于有符号整数,扩展前后表示的十进制整数一定也会是不变的,证明如下:
截断
直接将其位模式上的高位丢弃。
两种转换同时存在
当既有有符号和无符号的转换,又有扩展或者截断时,要先改变大小,再进行有符号和无符号之间的转换。这是C语言标准规定的。
先在位模式的视角直接相加,如果溢出了,则把溢出的位数直接截断。
无符号整数
位数为 时, 和 相加,得到 。即如果要溢出了就减去 。
设得到的结果为 ,如果 或者 ,则说明发生了溢出。
有符号整数
位数为 时, 和 相加,如果发生了正溢出就减去 ,如果发生了负溢出就加上 。
设得到的结果为
对于有符号整数 , 与 位级表示相同。
有符号和无符号整数之间相乘
都视为相同位级表示的无符号整数,然后相乘,得出的结果如果有溢出则截断。
乘上常数
对于要乘上的数字是一个常数的时候,则可以把乘法转化为若干个位运算来加速运算。
如果要乘上 ,则只需要左移 位。于是我们可以把任何的常数拆成若干个 的幂次相加或相减。
如 ,则 。
同时,,则 。
更一般的,我们可以将常数 的二进制视为若干个连续0或者连续1的块交替的形式:。
考虑一组从位位置 到位位置 的连续的 (),这一部分对乘积的影响有如下两种形式:
判断是否溢出
对于 和 相乘,令其结果为 。
如果 为 ,显然不会发生溢出。
反之,如果 p/x!=y
则说明发生溢出。
无符号
对于除以 且下取整,直接逻辑右移 位即可。
有符号
对于除以 ,如果直接算数右移 位,那么得到的结果是向下取整的。
但C语言中的负数做除法时,并不是向下取整,是向零取整的。
当被除数为正时,没什么问题。
当被除数为负时,我们先加上一个偏置值 ,再算数右移 位。
对于一个小数 ,我们可以将其表示为一个二进制的小数:
于是
然后,类似与科学计数法,我们可以把上述的二进制小数表示为:
编码时,从高位到低位依次是:
数据类型 | ||
---|---|---|
float | 8 | 23 |
double | 11 | 52 |
然后,浮点数在编码时分为如下的几种类型
情况1:规格化的值
编码阶码的部分既不全为 也不全为 。
情况2:非规格化的值
编码阶码的部分全为 。
情况3:特殊值
当编码阶码的部分全为 ,且编码尾数的部位全为 时,其表示为无穷大
但如果编码尾数的部分不全为 ,则表示 NaN
示例
对于一个 位,, 的小数,其示例如下:
同时,应当注意到,如果将一个浮点数的位级表示视为一个无符号整数,他们的大小顺序是相同的。对于负数的浮点数则相反
对于一般情况,正常进行四舍五入。如果一个数,恰好位于可能要舍入的两个数中间,则舍入到最后一位为偶数的那个数。
例如,舍入到小数点后两位
原数 | 舍入到的数 | 原因 |
---|---|---|
2.8949999 | 2.89 | 不到一半,正常四舍五入 |
2.8950001 | 2.90 | 超过一般,正常四舍五入 |
2.8950000 | 2.90 | 刚好在一半时,保证最后一位是偶数,所以向上舍入 |
2.8850000 | 2.88 | 刚好在一半时,保证最后一位是偶数,所以向下舍入 |
10.00011 | 10.00 | 不到一半,正常四舍五入 |
10.00110 | 10.01 | 超过一般,正常四舍五入 |
10.11100 | 11.00 | 刚好在一半时,保证最后一位是偶数,所以向上舍入 |
10.10100 | 10.10 | 刚好在一半时,保证最后一位是偶数,所以向下舍入 |
cpp123456789/*
* bitXor - x^y using only ~ and &
* Example: bitXor(4, 5) = 1
* Legal ops: ~ &
* Max ops: 14
*/
int bitXor(int x, int y) {
return (~(x&y))&(~((~x)&(~y)));
}
求 tmin
cpp123456789/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
return (1<<31);
}
我们发现,Tmax + 1 和 ~Tmax 是相等的。不过,-1
也有这个特点,所以需要特判一下
cpp12345678910/*
* isTmax - returns 1 if x is the maximum, two's complement number,
* and 0 otherwise
* Legal ops: ! ~ & ^ | +
* Max ops: 10
* Rating: 1
*/
int isTmax(int x) {
return (!(!(x^(~0))))&(!((x+1)^(~x)));
}
我们发现,!
这个运算符用来搞判断是非常好用的
我们每次把位级表示分成两半,然后把让低位的那一半与上高位的那一半,反复下去,即可求出
cpp123456789101112131415/*
* allOddBits - return 1 if all odd-numbered bits in word set to 1
* where bits are numbered from 0 (least significant) to 31 (most significant)
* Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 12
* Rating: 2
*/
int allOddBits(int x) {
x=x&(x>>16);
x=x&(x>>8);
x=x&(x>>4);
x=x&(x>>2);
return (x>>1)&1;
}
cpp12345678910/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return (~x)+1;
}
cpp1234567int isAsciiDigit(int x) {
int flag1 = !((x>>4)^(0x03));
int flag2 = x&(1<<3);
int flag3 = x&(1<<2);
int flag4 = x&(1<<1);
return flag1&((!flag2)|((!flag3)&(!flag4)));
}
首先把 x 转为要么为 0 要么为 1 的条件。
然后生成一个 msk,其为 flag+(~0)
。这样的话,如果 flag 为 1,msk 就为 0,反之,msk 就为各位都为 1 的一个数。
然后通过 msk 即可按条件返回不同的数。
cpp12345678910111213141516/*
* conditional - same as x ? y : z
* Example: conditional(2,4,5) = 4
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 16
* Rating: 3
*/
int conditional(int x, int y, int z) {
int flag = !(!x);
int msk;
msk = flag+(~0); //flag=0 -> msk=111... or msk = 0
int t1 = msk&z; //flag=0 -> c1=z or c1=0
msk = (!flag)+(~0); //flag=1 -> msk=111... or msk=0
int t2 = msk&y; //flag=1 -> c2=y or c2=0
return t1^t2;
}
cpp12345678910111213141516171819/*
* isLessOrEqual - if x <= y then return 1, else return 0
* Example: isLessOrEqual(4,5) = 1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 24
* Rating: 3
*/
int isLessOrEqual(int x, int y) {
int d=x+((~y)+1);
int flag1=(d>>31)&1;
int flag2=!d;
int sym1=(x>>31)&1;
int sym2=(y>>31)&1;
int flag3=sym1^sym2;
int p1=sym1&(!sym2);
int msk=flag3+(~0);
int p2=msk&(flag1|flag2);
return p1|p2;
}
类似上面的 allOddBits,我们判断奇数位上是否存在 1 和偶数位上是否存在 1
cpp1234567891011121314151617/*
* logicalNeg - implement the ! operator, using all of
* the legal operators except !
* Examples: logicalNeg(3) = 0, logicalNeg(0) = 1
* Legal ops: ~ & ^ | + << >>
* Max ops: 12
* Rating: 4
*/
int logicalNeg(int x) {
x=x|(x>>16);
x=x|(x>>8);
x=x|(x>>4);
x=x|(x>>2);
int flag1=1^(x&1);
int flag2=1^((x>>1)&1);
return flag1&flag2;
}
类似二分的思想,然后再套上条件运算
cpp12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061/* howManyBits - return the minimum number of bits required to represent x in
* two's complement
* Examples: howManyBits(12) = 5
* howManyBits(298) = 10
* howManyBits(-5) = 4
* howManyBits(0) = 1
* howManyBits(-1) = 1
* howManyBits(0x80000000) = 32
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 90
* Rating: 4
*/
int howManyBits(int x) {
int ans=0;
int flag1=!((x>>31)&1);
int msk1=flag1+(~0);
int p1=msk1&(~x);
int p2=(~msk1)&(x);
x=p1^p2;
int m1=1;
int m2=(1<<2)+(~0);
int m4=(1<<4)+(~0);
int m8=(1<<8)+(~0);
int m16=(1<<16)+(~0);
int now=x;
int tmp,flag,msk;
//16
tmp=now>>16;
flag=!(!tmp);
msk=flag+(~0);
ans=ans+((~msk)&(16));
now=((msk&(now&m16))^((~msk)&(tmp)));
//8
tmp=now>>8;
flag=!(!tmp);
msk=flag+(~0);
ans=ans+((~msk)&(8));
now=((msk&(now&m8))^((~msk)&(tmp)));
//4
tmp=now>>4;
flag=!(!tmp);
msk=flag+(~0);
ans=ans+((~msk)&(4));
now=((msk&(now&m4))^((~msk)&(tmp)));
//2
tmp=now>>2;
flag=!(!tmp);
msk=flag+(~0);
ans=ans+((~msk)&(2));
now=((msk&(now&m2))^((~msk)&(tmp)));
//1
tmp=now>>1;
flag=!(!tmp);
msk=flag+(~0);
ans=ans+((~msk)&(1));
now=((msk&(now&m1))^((~msk)&(tmp)));
flag=!(!now);
msk=flag+(~0);
ans=ans+((~msk)&(1));
return ans+1;
}
我们发现,对于非规格化的浮点数,乘二就相当于把尾数部分左移一位
对于规格化数,把阶码部分加上 1 即可
cpp1234567891011121314151617181920212223242526/*
* floatScale2 - Return bit-level equivalent of expression 2*f for
* floating point argument f.
* Both the argument and result are passed as unsigned int's, but
* they are to be interpreted as the bit-level representation of
* single-precision floating point values.
* When argument is NaN, return argument
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned floatScale2(unsigned uf) {
unsigned s=(uf>>31);//1
unsigned e=((uf<<1)>>24);//8
unsigned m=((uf<<9)>>9);//23
if(!e){
return (s<<31)|(uf<<1);
}else{
if(e==0xfe) return ((s<<8)|(0xff))<<23;
else if(e==0xff){
if(m) return uf;
return ((s<<8)|(0xff))<<23;
}
else return (s<<31)|((e+1)<<23)|m;
}
}
非规格化数一定全小于 1,规格化数中的一部分也是小于 1 的
cpp1234567891011121314151617181920212223242526272829303132/*
* floatFloat2Int - Return bit-level equivalent of expression (int) f
* for floating point argument f.
* Argument is passed as unsigned int, but
* it is to be interpreted as the bit-level representation of a
* single-precision floating point value.
* Anything out of range (including NaN and infinity) should return
* 0x80000000u.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
int floatFloat2Int(unsigned uf) {
unsigned s = (uf>>31);//1
unsigned e = ((uf<<1)>>24);//8
unsigned m = ((uf<<9)>>9);//23
if(!e) return 0;
if(e<127) return 0;
e=e-127;
if(e>=32) return 0x80000000u;
if(e<=23) m = m>>(23-e);
else m = m<<(e-23);
m = (1<<e)|m;
if(s){
if(m>0x80000000u) return 0x80000000u;
else if(m==0x80000000u) return (1<<31);
else return (~m)+1;
}else{
if(m>=0x80000000u) return 0x80000000u;
else return m;
}
}
cpp123456789101112131415161718192021222324/*
* floatPower2 - Return bit-level equivalent of the expression 2.0^x
* (2.0 raised to the power x) for any 32-bit integer x.
*
* The unsigned value that is returned should have the identical bit
* representation as the single-precision floating-point number 2.0^x.
* If the result is too small to be represented as a denorm, return
* 0. If too large, return +INF.
*
* Legal ops: Any integer/unsigned operations incl. ||, &&. Also if, while
* Max ops: 30
* Rating: 4
*/
unsigned floatPower2(int x) {
if(x<-149) return 0;
if(x>127) return 0x7f800000u;
if(x<-126){
int d=-x-126;
return 1<<(23-d);
}else{
int d=x+127;
return d<<23;
}
}