C语言中整型的减法


在C语言中,我们常常会用到加减等基本算术运算。我们知道,计算机在计算时,首先需要把数值转换成二进制,而后对二进制数进行运算。由于绝大多数机器都是使用二进制补码方式来存放整型的,因此下面讨论假设整型是以二进制补码方式表示的。二进制加法相当容易理解,简单的将各位相加,并处理好进位就可以了;对于二进制减法(为了方便叙述,这里设定整型字长为4位),则并不是那么直观了:\(0010_{2} – 0101_{2}\)(即十进制的\(2_{10} – 5_{10}\))的计算过程是怎样的呢?

实际上,在计算机中,当遇到诸如x-y这样的表达式时,并不是直接进行减法运算的,而是将其转换成形如x+(-y)这样的加法算式,而后再进行加法运算。拿上面的\(0010_{2} – 0101_{2}\)说,首先需要将\(0101_{2}\)转成其负数(-y),即转成\(1011_{2}\),然后再进行加运算,得到结果为\(1101_{2}\),即十进制数-3。

上面举的例子是有符号整数的,那么对于无符号整数,是否遵循同样的规则呢?下面我们举例来说明:

int x = 5;
int y = -1;
unsigned z = (unsigned)x - (unsigned)y;
printf("z=%u, (int)z=%d\n", z, (int)z);

不实际运行,自己计算一下z的值吧。

正确的答案是:z=6,你算对了吗?想知道为什么?OK,那就继续往下看吧!

首先,我们约定:对于任意整数a,ua表示a的无符号值。根据这个定义,我们可以得到:当a>=0时,ua=a;当a<0时,ua=\(a_{w-1}2^w + a\),其中w指a的字长。

对于上面代码片段中的表达式“(unsigned)x – (unsigned)y”,我们可以写成“ux – uy”,进一步的,当计算机要计算这个表达式的值时,会将其转换成“ux + (-uy)”。根据上面a与ua的关系,我们可以得到:\(ux + (-uy) = ux + (-(y_{w-1}2^w + y)) = ux + (-y_{w-1}2^w – y)\)。当y>=0时,符号位\(y_{w-1}\)为0,即ux – uy = ux + (- y);当y<0时,符号位\(y_{w-1}\)为1,ux – uy = ux + (\(-2^w – y\))。由于整型字长为w,无论是\(2^w\)还是\(-2^w\),其低w位均为0,由于截断(丢弃w位及以上位的数据),可知\(-2^w – y = (-2^w) + (-y) = -y\)。所以,对于任意值y,都有\(ux – uy = ux + (-y)\)。对ux做同样的展开:\(ux – uy = (x_{w-1}2^w + x) + (-y) = x + (-y)\)。

至此,我们得到最终结论:在C语言中,对于字长为w的两整数,有:\[ux – uy = x + (-y)\]\[ux + uy = x + y\]

Author: Rex Shen

Created: 2014-07-17 Thu 14:15

Emacs 24.3.1 (Org mode 8.2.7b)

Validate

Leave a comment

Your email address will not be published. Required fields are marked *