写在前面
这是2024.04班的,不保证其它班/以后题目都一样,请额外注意喵!
部分思路源于其它大佬,感谢超级大神 @Fediory 叠的倾力帮助!另感谢 鸭子青蛙 等热心群友老师们。
思路比较简略,加油,我相信你。(况且我没法完全还原我推的思路了,只能尽量拿正确思路的启发你们一下)
本文目前作为实验报告被提交,提交信息中含有一段随机值,其SHA1摘要为:4d3d77fd6fb2951a68d9b0ec71f75c91ea0213ba
圆括号代表我对难度的评分,方括号数字代表最优解个数,加号+
代表优化难度。
*是优化提示,!是优化解答,没有完整标答,多多思考喵。
最重要的提示:加法融合了溢出等多种逻辑,与各类优化相关
变量名全是错的😈️我故意的😡自己按照思路写!copy不好!
实在不会请善用各类交流群捏。
打榜,爽!
题意整理
- LsbZero —— 将最低位设为0
- Tmax —— 返回补码数字的最大正数
- UpperBits —— 生成一个高n位为1的掩码 ( 0 ≤ k ≤ 32 ) (0 \leq k \leq 32) (0≤k≤32)
- fitsBits —— x能否存进n位补码里?能返回1,不返回0
- isNegative ——
n < 0
? - isNotEqual ——
x != y
? - mul3div2 ——n * 3截断,然后除2,向零取整
- bitMask —— 生成一个掩码,二进制从low到high位是1(两端均含)(位序数从右边起,从0起)
- isAsciiDigit —— 字符编码x是不是ASCII的数字?(数字的编码 0x30 ≤ n u m ≤ 0x39 \text{0x30} \leq num \leq \text{0x39} 0x30≤num≤0x39)
- isNonNegative ——
n >= 0
? - logicOr ——
x || y
? - rewpw2 ——
x % (1<<n)
取余数。取余要求余数要么是0,要么和被除数的符号相同。 - satMul3 —— 类似16,但是这次计算的是乘3
- logicNeg —— 不使用
!
来实现!
- float_f2i —— 用unsigned int uf 存了个 ieee754 32位浮点数 的二进制表示,请把它向下取整转成整数。
- satAdd —— 计算a + b,如果结果为正但是太大溢出了,返回0x7fff ffff,负溢出了则返回0x8000 0000。
- tc2sm —— 计算原码。
0. 好用的板子
请尽可能地理解这些模板的原理。
部分板子源于大佬们。
这些板子的推导大部分是基于“注意到”的,很难说明它们到底是怎么想出来的。
将低n位置0
a & ~...
比较标准但是有点麻烦
a >> n << n
n大于8时很好用+省符号
相反数
(~n + 1)
即-n,补码不解释。
优化提示:取负数常数的时候请使用 (~(n-1))
并且提前求出 n-1 。
取01
我们发现符号表里有个!,逻辑非,好耶。
复习一下:在C语言中没有所谓的布尔类型,!生成的False总是0,True总是1;0总是视作False,大部分机器把非零都视作True。少部分机器<0也视作False。在其他地方用出bug的时候记得想一想这个。
n = 0的时候!n = 1,!!n = 0
n不为0时!n = 0,!!n = 1
记住 !!n
喵
符号相关板子
sign = a >> 31
—— 提取符号,如果a为负,得到-1或全1;如果a为正,得到全0。算术右移。
(a + sign) ^ sign
—— 乘以符号,可以用来取绝对值。原理:sign为-1就减1然后异或全1取反,sign为0就没用。
选择器板子
(a & sign) | (b & ~sign)
——sign 为-1时返回a,为0时返回b
喜欢独立思考的别往下看了嗷
1. LsbZero (0/5) [2]
要求:将最低位设为0。
可用方法:
a & ~1;
a >> 1 << 1;
2. Tmax (0/5) [2]
要求:返回补码最大值,即2147483647 0x7fff ffff
。
用最高位为1取反即可。
~(1<<31);
3. UpperBits (1/5) [6]
要求:生成一个高k位为1的掩码。 ( 0 ≤ k ≤ 32 ) (0 \leq k \leq 32) (0≤k≤32)
注意:k可能为0也可能为32,为0要特殊处理,32没法移动,需要处理这两种边界情况。
既然高k位为1,考虑用符号右移从 1 << 31
拉出若干个 1 吧。>>1就有两个1了,所以我们需要k - 1个,但是没有负数和减号怎么办捏,用取反~0得到-1即可。
1 << 31 >> (k + ~0)
但是我们发现k = 0的时候会出问题,我们就想,如果k=0的时候左边那个1是0就好了,但是k最低位并不总是1,不能用&1🤔
那就!!n取1板子。
那么
!!k << 31 >> (k + ~0);
收工。
4. fitsBits (3/5) [6]
要求:判断a能不能存在n位补码里。
首先根据课本可知,扩展大小并在左边添加和m个符号位相同的bit不会改变表示的数。(证明我就不写了,好好看课本!)
同理,从左边删除m个相同bit也不会改变表示的数。
换言之,如果能用n个bit表示,那么,从右起数n-1个bit,不管它们,其它的bit必须是相同的bit,当然,这些bit肯定和原来int的符号位一样。减一是保留符号位。
嗨嗨,还是符号右移,我们直接 a >> n - 1
把 n - 1 个bit挤掉,然后和 a >> 31
比较是否相等。
比较相等请参见 6 isNotEqual 捏
5. isNegative (0/5) [2]
要求:n负数时返回1。
提取符号之后,正是全0、负是全1,容易看出&1取低位即可。
6. isNotEqual (1/5) [3]
要求:a != b返回1,a == b 返回 0
异或可能大家不熟悉,我再讲一次:a b某位不同时,该位结果为1,相同时结果为0。
那么容易想到,a ^ b在a b相同时返回全0,否则返回必带1,然后!!板子即可。
7.mul3div2 (1/5) [6]
要求:返回 n * 3 / 2 的结果(并且符合机器乘除的行为)
请注意,除2不能完全等价于>>1,除2会向0取整,但是>>1会向下取整!((-1)/2 为 0,-1 >> 1 为 -1
!!!)
所以很简单我们给乘积为负数的加个1再右移即可。(-2 + 1 => -1,-1>>1 =>-1,-1+1=>0,0>