计算机基础5

399 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情

第1讲 整数乘法运算

整数的乘运算

通常,高级语言中两个n位整数相乘得到的结果通常也是一个n位整数,也即结果只取2n位乘积中的低n位。

image.png

可用无符号乘来实现带符号乘,但高n位无法得到,故不能判断溢出。

image.png 硬件不判溢出,仅保留2n位乘积,供软件使用 如果程序不采用防止溢出的措施,且编译器也不生成用于溢出处理的代码,就会发生一些由于整数溢出而带来的问题。 指令∶分无符号数乘指令、带符号整数乘指令

乘法指令不生成溢出标志,编译器可使用2n位乘积来判断是否溢出!

变量和常数间的乘运算

整数乘法运算比移位和加法等运算所用时间长,通常一次乘法运算需要多时钟周期,而一次移位、加法和减法等运算只要一个或更少的时钟周期,因此,编译器在处理变量与常数相乘时,往往以移位、加法和减法的组合运算来代替乘法运算。

不管是无符号数还是带符号整数的乘法,即使乘积溢出时,利用移位和加减运算组合的方式得到的结果都是和采用直接相乘的结果是一样的。

第二讲 整数除法运算

  • 因为商的绝对值不可能比被除数的绝对值更大,因而不会发生溢出,也就不会像整数乘法运算那样发生整数溢出漏洞。
  • 因为整数除法,其商也是整数,所以,在不能整除时需要进行舍入,通常按照朝0方向舍入,即正数商取比自身小的最接近整数(Floor ,地板),负数商取比自身大的最接近整数(Ceiling,天板)。
  • 7/0等整除0时会发生异常,此时,需要调出操作系统中的异常处理程序来处理

例子:

int a = Ox80000000;int b = a / -1; printf("%d\n". b); 运行结果为-2147483648

int a = Ox80000000;int b = -1; int c = a / b; printf(" %d\n", c);

运行结果为“Floating point exception”,显然CPU检测到了异常

为什么呢?

t:对于第一段代码,a/-1被优化成取负指令neg,故未发生除法溢出。但结果是错误的。而对于第二段代码,浮点异常,到后面会进行更加详细的学习。

  • 对于整数除法运算,由于计算机中除法运算比较复杂,而且不能用流水线方式实现,所以一次除法运算大致需要30个或更多个时钟周期,比乘法指令的时间还要长!

  • 为了缩短除法运算的时间,编译器在处理一个变量与一个2的幂次形式的整数相除时,常采用右移运算来实现。

    • 无符号:逻辑右移
    • 带符号:算数右移
  • 结果一定取整

    • 能整除时,直接右移得到结果,移出的全为0

      • 12/4=3 : 0000 1100>>2=0000 0011
      • -12/4=-3 : 1111 0100 >>2=1111 1101
    • 不能整除时,右移移出的位中有非0,需要进行相应处理

      • 无符号数、带符号正整数(地板)∶移出的低位直接丢弃带符号

        • 无符号数:14/4=3:0000 1110>>2=0000 0011
      • 负整数(天板)∶加偏移量(2k-1),然后再右移k位,低位截断(这里K是右移位数)

        • 带符号负整数:-14/4=-3

          • 此时还按上面的话会得出-4。应先纠偏,再右移:k=2,故(-14+2的2次方-1)/4=-3

image.png 分析一把:b是x的偏移量,当x为负时,b=31,当b为正时,b=0。

怎么得到b呢?x>>31即可解决。因为,x若是正数,前面表示符号的就全是0,x是负数,就全是1。意思就是说,x是正数,右移31位后取出最右边的5位也就是00000,所以此时b=0;x是负数,右移31位后取出最右边的5位也就是11111,所以此时b=31;

PS:&0x1F表示把最右边5位取出来