1. 程式人生 > >《深入理解計算機系統》 練習題2.32答案

《深入理解計算機系統》 練習題2.32答案

編寫函式tsub_ok的程式碼,引數是x和y,執行的運算是x-y,如果計算x-y不產生溢位,函式就返回1.假設你寫的程式碼如下:

int tsub_ok(int x, int y) {
    return tadd_ok(x,-y);
}

tadd_ok函式

int tadd_ok(int x, int y) {
    int sum = x + y;
    int neg_over = x < 0 && y < 0 && sum >= 0;//兩個數都為負數,但和卻為正數,說明負溢位
    int pos_over = x >= 0 &&
y >= 0 && sum < 0;//兩個數都為正數,但和卻為負數,說明正溢位 return !neg_over && !pos_over;//有一個取反後為0,就會返回0 }

此函式能檢測到兩個數相加,若發生溢位,則返回0,否則返回1。

錯誤原因

如果y的值為INT_MIN(231-2^{31}),那麼-y則應為2312^{31},但實際效果是-y還是231-2^{31}。這樣就會造成程式錯誤。

因為這裡發生了正溢位。可以從兩種角度進行解釋:
給出一些常識,int是4個位元組的,所以一共有32位二進位制。

1)補碼截斷。
初始時y為INT_MIN(

231-2^{31})。

補碼的值 1000 0000 0000 0000 0000 0000 0000 0000
對應權值 231-2^{31}

左邊是高有效位,右邊是低有效位。標紅色的為補碼的符號位。

取反後-y為2312^{31}

補碼的值 0000 0000 1000 0000 0000 0000 0000 0000 0000 0000
對應權值 2312^{31}

取反32位二進位制無法表示2312^{31}了,所以得拓展成更多位了,注意多了多少位已經不重要了,重要的是,現在的第32位已經不是符號位了,所以第32位的權值則為2312^{31}而不是負的了。

但最終變數是int型的,所以必須截斷。

補碼的值 1000 0000 0000 0000 0000 0000 0000 0000
對應權值 231-2^{31}

所以,最終-y的值為231-2^{31}

2)溢位時運算。
一般來說,正溢位時,則可以認為運算結果得到了第33位二進位制的權值2322^{32}。所以就應該是:
初始時,y為231-2^{31}
取反後,-y為2312^{31}
因為正溢位,所以減去2322^{32},-y為2312^{31}-2322^{32},最終為231-2^{31}

正確版本

#include <stdio.h>  
#include "stdafx.h"
#include <iostream>

using namespace std;

int tadd_ok(int x, int y) {
    int sum = x + y;
    int neg_over = x < 0 && y < 0 && sum >= 0;//兩個數都為負數,但和卻為正數,說明負溢位
    int pos_over = x >= 0 && y >= 0 && sum < 0;//兩個數都為正數,但和卻為負數,說明正溢位

    return !neg_over && !pos_over;//有一個取反後為0,就會返回0
}

int tsub_ok(int x, int y) {
    if(y == INT_MIN)//只要y為INT_MIN,就直接返回0
    	return 0;
    return tadd_ok(x, -y);
}