一、实验目的
本实验目的是加强学生对位级运算的理解及熟练使用的能力。
二、报告要求
本报告要求学生把实验中实现的所有函数逐一进行分析说明,写出实现的依据,也就是推理过程,可以是一个简单的数学证明,也可以是代码分析,根据实现中你的想法不同而异。
三、函数分析
符号只能使用位运算符 ! ~ & ^ | + << >> ,不能使用 if, else, ?:, while
bitXor函数
函数要求:
| 函数名 | bitXor |
|---|---|
| 参数 | int , int |
| 功能实现 | x^y |
实现分析:
a^b = ~( ~(a&~b)&(~(~a&b)))
异或的逻辑是 x ^ y = (x AND ~y) OR (~x AND y)
根据de morgan律:A | B = (A & ~B)
函数实现:
int bitXor(int x, int y) {
return ~(~(x&~y)&(~(~x&y)));
}
getByte函数
函数要求:
| 函数名 | getByte |
|---|---|
| 参数 | int , int |
| 功能实现 | 在word x中提取第n个字节 |
实现分析:
getByte(x,n) = (x>>(n<<3))&0xff
从一个 32 位整数中取出第 n 个字节(0 表示最低字节)。
- 每个字节是 8 位,所以要移动
n * 8位,即n << 3。 - 再用掩码
0xFF(11111111)保留最后 8 位。
函数实现:
int getByte(int x, int n) {
return (x>>(n<<3))&0xff;
}
logicalShift函数
函数要求:
| 函数名 | logicalShift |
|---|---|
| 参数 | int , int |
| 功能实现 | 将x向右移n位 |
实现分析:
在函数中新定义了两个变量y和z,其中:
普通的 >> 是算术右移,会补符号位(即1或0)。
逻辑右移则始终补 0。
解决方法:
- 先算术右移
x >> n; - 再用掩码清除被符号位填充的部分。
掩码思路:
(1 << 31) 是最高位为 1 的数。
((1 << 31) >> n) << 1 构造出 n 位的 1,再取反即为低 32-n 位为1的掩码。
函数实现:
int logicalShift(int x, int n) {
int mark = 0x01 << 31;
int y = (x & mark);
int z = ~((y>>n)<<0x1);
return (x>>n) & z;
}
bitCount函数
函数要求:
| 函数名 | bitCount |
|---|---|
| 参数 | int |
| 功能实现 | 计算输入字节中为1的bit数 |
实现分析:
- 原理:一个2bit的二进制数,其所有的组合有00, 01, 10, 11。若要计算这个2bit的数的二进制有多少个1,则可以用这个数减去其二进制第二个位上的数字,得到的便是这个2bit数字的二进制中的1个个数。
- 核心思想(并行计数 / 分治):
把 32 位分成很多小块,先在最小粒度上对相邻位成对计数,然后把结果在更大粒度上合并,逐层累加,最终得到总和。这样做避免了循环和条件判断,全部用位运算和加法完成,速度快且合法操作数有限。
- 掩码(便于分组):
- mask1 = 0x55555555 = 二进制每 2 位为
01 01 01 ...
用于把每对位分别取出(位0与位1配对)。 - mask2 = 0x33333333 = 每 4 位为
0011 0011 ...
用于把每 4 位当作两组 2 位的和来合并。 - mask4 = 0x0F0F0F0F = 每 8 位为
00001111 ...
用于把每 8 位当作两组 4 位的和来合并。
函数实现:
int bitCount(int x) {
int m8 = 0x55;
int mask1 = m8 | (m8 << 8) | (m8 << 16) | (m8 << 24);
int m3 = 0x33;
int mask2 = m3 | (m3 << 8) | (m3 << 16) | (m3 << 24);
int m0f = 0x0f;
int mask4 = m0f | (m0f << 8) | (m0f << 16) | (m0f << 24);
x = x + (~(((x >> 1) & mask1)) + 1);
x = (x & mask2) + ((x >> 2) & mask2);
x = (x + (x >> 4)) & mask4;
x = x + (x >> 8);
x = x + (x >> 16);
return x & 0x3f;
}
conditional函数
函数要求:
| 函数名 | conditional |
|---|---|
| 参数 | int , int , int |
| 功能实现 | 实现x ? y : z |
实现分析:
核心思想是:
我们想构造一个掩码(mask),它是全 1 或全 0。
- 当
x ≠ 0时 →mask = 0xFFFFFFFF - 当
x == 0时 →mask = 0x00000000
制造掩码:
mask = !!x; // 把 x 变成 0 或 1
mask = ~mask + 1; // 扩展成全 0 或全 1
!!x:两次逻辑非,确保结果只有 0 或 1- 比如:
x = 5 → !!x = 1
x = 0 → !!x = 0
- 比如:
~mask + 1:- 当 mask = 0 → (~0 + 1) = 0
- 当 mask = 1 → (~1 + 1) = 0xFFFFFFFF
函数实现:
int conditional(int x, int y, int z) {
int mask = (!!x << 31) >> 31;
return (mask & y) | (~mask & z);
}
tmin函数
函数要求:
| 函数名 | tmin |
|---|---|
| 参数 | void |
| 功能实现 | 返回最小补码 |
实现分析:
最小的补码就是0x10000000,直接返回
函数实现:
int tmin(void) {
return 0x1<<31;
}
fitsBits函数
函数要求:
| 函数名 | fitsBits |
|---|---|
| 参数 | int , int |
| 功能实现 | 若x能用n个bit表示则返回1,否则返回0 |
实现分析:
整体思路:
- 用 n 位能表示的数,其符号扩展后应与原值相同;
- 左移丢弃高位再右移回来,看是否变了。
函数实现:
int fitsBits(int x, int n) {
int shift = 32 + (~n + 1); // 32 - n
return !(((x << shift) >> shift) ^ x);
}
dividePower2函数
函数要求:
| 函数名 | dividePower2 |
|---|---|
| 参数 | int , int |
| 功能实现 | 计算x/(2^n),其中0<=n<=30 |
实现分析:
正数右移n位即可;
负数需要补偿偏差使结果"向零取整"(而非向下取整)。
函数实现:
int dividePower2(int x, int n) {
int sign = x >> 31;
int bias = (1 << n) + ~0; // 2^n - 1
int add = sign & bias;
return (x + add) >> n;
}
negate函数
函数要求:
| 函数名 | negate |
|---|---|
| 参数 | int |
| 功能实现 | 取相反数-x |
实现分析:
直接按位取反加1即可
函数实现:
int negate(int x) {
return ~x+1;
}
howManyBits函数
函数要求:
| 函数名 | howManyBits |
|---|---|
| 参数 | int |
| 功能实现 | 计算一个整数 x 用补码表示所需的最少二进制位数。返回表示x所要用的最小bit数 |
实现分析:
| x | 二进制补码 | 最少位数 |
|---|---|---|
| 12 | 01100 |
5 |
| 298 | 0100101010 |
10 |
| -5 | 1011 |
4 |
| 0 | 0 |
1 |
| -1 | 1 |
1 |
| 0x80000000 | 1000...000 |
32 |
要知道要多少位表示 x,实际上是找:
- 对于正数:最高的 1 在第几位
- 对于负数:最高的 0 在第几位(因为负数补码前面是 1)
所以要对数处理:
- 对正数:不变;
- 对负数:按位取反,相当于找"最高的 1"。
然后用位级的二分来进行查找最高的1
最终 b16 + b8 + b4 + b2 + b1 + b0 + 1 就是所需的位数。
加 +1 是因为还要包含符号位。
函数实现:
int howManyBits(int x) {
int b16, b8, b4, b2, b1, b0;
int sign = x >> 31; /* sign = 0 (x >= 0) or -1 (x < 0) */
x = (sign & ~x) | (~sign & x); /* if x negative, set x = ~x, else keep x */
b16 = !!(x >> 16) << 4; /* if top 16 bits nonzero, add 16 */
x >>= b16;
b8 = !!(x >> 8) << 3; /* if top 8 bits nonzero, add 8 */
x >>= b8;
b4 = !!(x >> 4) << 2; /* if top 4 bits nonzero, add 4 */
x >>= b4;
b2 = !!(x >> 2) << 1; /* if top 2 bits nonzero, add 2 */
x >>= b2;
b1 = !!(x >> 1); /* if top 1 bit nonzero, add 1 */
x >>= b1;
b0 = x;
return b16 + b8 + b4 + b2 + b1 + b0 + 1;
}
isLessOrEqual函数
函数要求:
| 函数名 | isLessOrEqual |
|---|---|
| 参数 | int , int |
| 功能实现 | 若x<=y则返回1,否则返回0 |
实现分析:
判断大小转换成看两数之差 y - x >= 0,但是两数异号直接计算可能会溢出
所以我们必须先判断 x 与 y 的符号是否相同:
情况1:x 与 y 符号不同
- 如果 x 是负的,y 是正的 → 一定成立 (x ≤ y)
- 如果 x 是正的,y 是负的 → 一定不成立 (x ≤ y)
情况2:x 与 y 符号相同
- 用正常的差值判断:
y - x >= 0
函数实现:
int isLessOrEqual(int x, int y) {
int sx = x >> 31; /* sign bit of x: 0 or -1 */
int sy = y >> 31; /* sign bit of y */
int diff = sx ^ sy; /* 1 if signs differ */
int sub = y + (~x + 1); /* y - x */
int ssub = sub >> 31; /* sign of (y - x) */
/* If signs differ and x negative, x <= y. Else if signs same, check y-x >= 0 */
int res = (diff & sx) | ((!diff) & (!ssub));
return !!res;
}
intLog2函数
函数要求:
| 函数名 | intLog2 |
|---|---|
| 参数 | int |
| 功能实现 | 计算log2(x)并向下取整 |
实现分析:
这个问题的实质是找最高位的1在哪一位
用二分法查找最高位的方式来实现:
设一个变量 ans = 0,表示当前找到了最高位的下标。
从高位向低位检测是否有 1:
- 如果
x >> 16不为 0 → 说明最高位至少在 [16, 31],那么ans += 16 - 再检测
x >> 8不为 0 →ans += 8 - 再检测
x >> 4不为 0 →ans += 4 - 再检测
x >> 2不为 0 →ans += 2 - 再检测
x >> 1不为 0 →ans += 1
ans 就是 ⌊log₂(x)⌋。
函数实现:
int intLog2(int x) {
int ans = 0;
int b16, b8, b4, b2, b1;
/* check top 16 bits */
b16 = !!(x >> 16) << 4; /* if high 16 bits set, add 16 */
ans += b16;
x >>= b16;
/* check top 8 bits */
b8 = !!(x >> 8) << 3;
ans += b8;
x >>= b8;
/* check top 4 bits */
b4 = !!(x >> 4) << 2;
ans += b4;
x >>= b4;
/* check top 2 bits */
b2 = !!(x >> 2) << 1;
ans += b2;
x >>= b2;
/* check top 1 bit */
b1 = !!(x >> 1);
ans += b1;
return ans;
}
floatAbsVal函数
函数要求:
| 函数名 | floatAbsVal |
|---|---|
| 参数 | unsigned |
| 功能实现 | 返回浮点数f的绝对值 |
| 要求 | 可以使用运算符, |
实现分析:
- 取绝对值:
清除符号位即可(符号位在最高位)。
即uf & 0x7FFFFFFF。 - 检测 NaN:
当指数全为 1(e = 0xFF000000)且尾数不为 0(f != 0)时,是 NaN。
函数实现:
unsigned floatAbsVal(unsigned uf) {
unsigned mask = 0x7FFFFFFF;
unsigned abs = uf & mask;
if (abs > 0x7F800000)
return uf;
return abs;
}
floatScale1d2函数
函数要求:
可以使用 integer/unsigned, operations, ||, &&. also if, while
| 函数名 | floatScale1d2 |
|---|---|
| 参数 | unsigned |
| 功能实现 | 返回0.5*f |
功能:
返回与表达式 0.5 * f位级等价的无符号整数。
- 输入
uf是一个单精度浮点数的位模式; - 输出同样是一个位模式;
- 若
uf是 NaN,则返回原值。
实现分析:
- 若输入为INF和NaN,那么直接返回
- 若exp>1,保留小数位然后直接把exp-1,阶码也就减少1,达到了*0.5的目的
- exp<=1,此时若直接把exp-1,那么exp将会小于等于0,实现不了f*0.5;
- 所以将uf右移1位实现"exp-1"(若exp=1那么将会得exp=0,若exp=0那么exp的值不变)和小数部分的f*0.5。
- 但是在这里要注意小数的舍入问题,若f的第0位和第1位都是1,那么需要给他加2。
函数实现:
unsigned floatScale1d2(unsigned uf) {
int exp = (uf & 0x7fffffff)>>23;
int sign = uf & 0x80000000;
if((uf&0x7fffffff) >= 0x7f800000) return uf;
if(exp > 1) return (uf&0x807fffff)|(--exp)<<23;
if((uf&0x3) == 0x3) uf = uf + 0x2;
return ((uf>>1)&0xbfffffff)|sign;
}
floatFloat2Int函数
函数要求:
可以使用 integer/unsigned, operations, ||, &&. also if, while
| 函数名 | floatFloat2Int |
|---|---|
| 参数 | unsigned |
| 功能实现 | 将f转换为int形式 |
实现分析:
- 提取符号位 S、指数 E 和尾数 F
- 计算真实指数
exp = E-127 - 恢复尾数隐含 1 → M
- 特殊情况:
- E=255 → NaN/∞ → 返回 0x80000000
- exp < 0 → |f|<1 → 返回 0
- exp > 30 → 超出 int → 返回 0x80000000
- 根据 exp 调整尾数得到整数部分:
- exp > 23 → 左移
- exp ≤ 23 → 右移
- 根据符号返回正负整数
函数实现:
int floatFloat2Int(unsigned uf) {
unsigned sign = uf >> 31;
int exp = ((uf >> 23) & 0xFF) - 127;
unsigned frac = uf & 0x7FFFFF;
unsigned M = frac | 0x800000;
unsigned val;
if (((uf >> 23) & 0xFF) == 0xFF)
return 0x80000000;
if (exp < 0)
return 0;
if (exp > 30)
return 0x80000000;
if (exp > 23) {
val = M << (exp - 23);
} else {
val = M >> (23 - exp);
}
return sign ? -val : val;
}