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;
}

看代码的时候,发现有人评论:

CSAPP-3e-Solutions/chapter2/2.75

can I ask one question?
why int64_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位的。这里蕴含着两个问题:

  1. 这个强制类型转换做了什么操作
  2. 为什么要做这个操作

其实我现在知道的是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,避免了整数溢出的问题。

其实这样就解决了这个问题的两个疑惑:

  1. 这个强制类型转换做了什么操作?

    答:将参数x的类型转换为 int64_t

  2. 为什么要做这个操作?

    答:想让x和y的乘积不溢出。这需要让结果类型为64位,这样就需要将两个参数进行升级。

    但是现在可以利用C语言的类型升级(涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别)来简化代码书写规则:只需要将其中一个参数x升级为64位的类型,另一个低级类型参数y在参与运算的过程中会被自动转换为64位的数据。

    这样两个64位的数据得到的结果是64位的数据,正好可以保存32位int类的乘法溢出的高位数据,解决了溢出问题。
    (存量数据,写于2024年2月4号)

posted @ 2024-02-20 19:18  上山砍大树  阅读(8)  评论(0编辑  收藏  举报