csapp-homework-2.75(1)
在做题目2.75的时候,发现课后习题是跟课文内容强关联的。所以我觉得后期优化csapp学习的方法就是,先快速浏览一遍内容,尽量按照习题去过基础内容。
2.75题目是关于补码和无符号整数的乘法运算,这里翻看课本重新回顾下之前的无符号和补码的乘法运算。
比较重要的是:补码的乘法结果 = 无符号数的乘法结果取模后再转换为补码形式的结果。
发现不会做,直接看答案:
/* * unsigned-high-prod.c */ #include <stdio.h> #include <assert.h> #include <inttypes.h> int signed_high_prod(int x, int y) { int64_t mul = (int64_t) x * y; return mul >> 32; } unsigned unsigned_high_prod(unsigned x, unsigned y) { /* TODO calculations */ int sig_x = x >> 31; int sig_y = y >> 31; int signed_prod = signed_high_prod(x, y); return signed_prod + x * sig_y + y * sig_x; } /* a theorically correct version to test unsigned_high_prod func */ unsigned another_unsigned_high_prod(unsigned x, unsigned y) { uint64_t mul = (uint64_t) x * y; return mul >> 32; } int main(int argc, char* argv[]) { unsigned x = 0x12345678; unsigned y = 0xFFFFFFFF; assert(another_unsigned_high_prod(x, y) == unsigned_high_prod(x, y)); return 0; }
看代码的时候,发现有人评论:
can I ask one question?
whyint64_t mul = (int64_t) x * y;
must here explicit cast then implicit cast?(I thought I have seen related contents in csapp, but search "explicit" found nothing related)
额,为什么在x * y的时候强制类型转换为64位的。这里蕴含着两个问题:
- 这个强制类型转换做了什么操作
- 为什么要做这个操作
其实我现在知道的是x和y的补码乘积会溢出(例如x = INT_MIN、y = INT_MIN时,x*y的结果会超出32位的范围),溢出后计算机会自动将其高32位去除,保留低32位。这样也等效于结果取2的32次方的模。
所以需要将其计算结果转为64位,然后才能保存乘法运算中溢出的高位数据。
但是这个(int64_t) x*y
,是先将x和y转为64位后(等价于(int64_t) x * (int64_t) y
)再运算呢还是将计算结果(等价于(int64_t)x*y
)转为64位呢?我不了解,因为我不清楚乘法运算符和类型转换符号的先后顺序,以及能否在得到结果后再强制类型转换。
那就首先解决这个类型转换和乘除法的优先级问题。
在菜鸟教程C 强制类型转换中,示例代码非常贴合现在我们遇到的问题:
#include <stdio.h> int main() { int sum = 17, count = 5; double mean; mean = (double) sum / count; printf("Value of mean : %f\n", mean ); }
下面对这个代码的解释也很到位:
这里要注意的是强制类型转换运算符的优先级大于除法,因此 sum 的值首先被转换为 double 型,然后除以 count,得到一个类型为 double 的值。
关于强制类型转换和乘除法的运算优先级,查询chat-gpt:
由于
x
被强制类型转换为int64_t
,整个表达式会采用更宽的整数类型(int64_t
)进行计算,因此y
会隐式地升级到int64_t
,避免了整数溢出的问题。
其实这样就解决了这个问题的两个疑惑:
-
这个强制类型转换做了什么操作?
答:将参数
x
的类型转换为int64_t
。 -
为什么要做这个操作?
答:想让x和y的乘积不溢出。这需要让结果类型为64位,这样就需要将两个参数进行升级。
但是现在可以利用C语言的类型升级(涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别)来简化代码书写规则:只需要将其中一个参数x升级为64位的类型,另一个低级类型参数y在参与运算的过程中会被自动转换为64位的数据。
这样两个64位的数据得到的结果是64位的数据,正好可以保存32位int类的乘法溢出的高位数据,解决了溢出问题。
(存量数据,写于2024年2月4号)
本文作者:上山砍大树
本文链接:https://www.cnblogs.com/shangshankandashu/articles/18023872
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步