这两天的工作需要涉及到对无符合数,有符号数作混合运算。
作了一些实验,发现自己写了这么多年的程序,以前对于符号数计算相关的理解居然仍存在一些盲点。
有符号数与无符号数混合运算可能有三种混合方式
1。操作数全为有符号数
如:
int a = -1;
int b = 2;
a + b;
2。操作数全为无符号数
如:
unsigned int a = (unsigned int)-1;
unsigned int b = 2;
a + b;
3。操作数混合了有符号数,无符号数
如:
int a = -1;
unsigned int b = 2;
a / b;
情形1和2就不用说了,因为运算中涉及的都是相同符号特征的操作数,计算过程中不会引起歧义。
而对于情形3,由于涉及到了符号特性相异的操作数,情况就有些复杂了。
这里先说一下,对于有些运算操作,是要区分有符号与无符号的情况的。比如有符号的除法与无符号
的除法,有符号的取模运算与无符号的取模运算,其计算语意是不同的,具体来说,有符号的除法在x86
平台上对应的汇编指令是idiv,而无符号的除法对应的则是div。
而对于另外一些操作运算,则是不区分有符号与无符号的,比如,加法,减法,乘法运算。
对于不同操作符与符号相关的情形可以通过下面的小程序来验证:
int main()
{
signed int n1 = 1;
signed int n2 = 2;
unsigned int u1 = 1;
unsigned int u2 = 2;
signed int a = n1 + n2;
unsigned int ua = u1 + u2;
signed int s = n1 - n2;
unsigned int us = u1 - u2;
signed int m = n1 * n2;
unsigned int um = u1 * u2;
signed int d = n1 / n2;
unsigned int d = u1 / u2;
signed int m = n1 % n2;
unsigned int um = u1 % u2;
return 0;
}
对这一段程序调用 g++ -S,生成相应的汇编文件,就会发现:
加法运算,无论是有符号还是无符号,对应的都是addl指令,
减法运算,对应的都是subl指令,
乘法运算,对应的都是imul指令,
有符号除法运算对应的是idiv指令,无符号除法对应的则是 div指令,
有符号数取模运算会用到idiv指令,无符号取模用的则是div指令。
所以回到初始的问题,情形1和2的行为是容易预期的,因为所有操作数都具有同样的符号特性,直接就
可以得出采用相应符号特性的运算类型。
对于情形3,因为涉及到不同符号数的混合计算,在计算之前需要先对操作数进行规整化的动
作,规整的原则就是如果操作数中存在至少一个无符号数,则所有操作数都被转化为无符号数,
运算操作也采用相应的无符号操作符进行,计算完的结果也是一个无符号数。
举例来说:
(unsigned int)a / (signed int)b 会采用无符号除法进行,其实质相当于
(unsigned int)a / (unsigned int)b
计算结果也是一个无符号数。
再进一步,对于运算-2 / -1,如果采用有符号数运算,结果是1,采用无符号数运算,结果则是0。
所以 (signed int)(-2) / (unsigned int)(-1)的结果就是0了。
除法,取模这样的操作符在不同的上下文语境里对应的语义动作也有所不同,而且这种差异还不
同于c++里的操作符重载在语言级别可见,而是要到更底层的汇编语言级别才可见,这多少就有
一些tricky,也容易诱使程序员犯错了。如果有机会我来设计一门语言,我想自己会尽量避免引
入这种tricky的东西的。
转自:
http://hi.baidu.com/yjpro/blog/item/4b09ed09336a60c83bc763f2 .html
对于这个问题,是因为两个数字相加的时候被规整为了无符号数(上面的文章中有提到),而a和b作为无符号数的值分别为
作了一些实验,发现自己写了这么多年的程序,以前对于符号数计算相关的理解居然仍存在一些盲点。
有符号数与无符号数混合运算可能有三种混合方式
1。操作数全为有符号数
如:
int a = -1;
int b = 2;
a + b;
2。操作数全为无符号数
如:
unsigned int a = (unsigned int)-1;
unsigned int b = 2;
a + b;
3。操作数混合了有符号数,无符号数
如:
int
unsigned int b = 2;
a / b;
情形1和2就不用说了,因为运算中涉及的都是相同符号特征的操作数,计算过程中不会引起歧义。
而对于情形3,由于涉及到了符号特性相异的操作数,情况就有些复杂了。
这里先说一下,对于有些运算操作,是要区分有符号与无符号的情况的。比如有符号的除法与无符号
的除法,有符号的取模运算与无符号的取模运算,其计算语意是不同的,具体来说,有符号的除法在x86
平台上对应的汇编指令是idiv,而无符号的除法对应的则是div。
而对于另外一些操作运算,则是不区分有符号与无符号的,比如,加法,减法,乘法运算。
对于不同操作符与符号相关的情形可以通过下面的小程序来验证:
int main()
{
signed int n1 = 1;
signed int n2 = 2;
unsigned int u1 = 1;
unsigned int u2 = 2;
signed int a = n1 + n2;
unsigned int ua = u1 + u2;
signed int s = n1 - n2;
unsigned int us = u1 - u2;
signed int m = n1 * n2;
unsigned int um = u1 * u2;
signed int d = n1 / n2;
unsigned int d = u1 / u2;
signed int m = n1 % n2;
unsigned int um = u1 % u2;
return 0;
}
对这一段程序调用
加法运算,无论是有符号还是无符号,对应的都是addl指令,
减法运算,对应的都是subl指令,
乘法运算,对应的都是imul指令,
有符号除法运算对应的是idiv指令,无符号除法对应的则是
有符号数取模运算会用到idiv指令,无符号取模用的则是div指令。
所以回到初始的问题,情形1和2的行为是容易预期的,因为所有操作数都具有同样的符号特性,直接就
可以得出采用相应符号特性的运算类型。
对于情形3,因为涉及到不同符号数的混合计算,在计算之前需要先对操作数进行规整化的动
作,规整的原则就是如果操作数中存在至少一个无符号数,则所有操作数都被转化为无符号数,
运算操作也采用相应的无符号操作符进行,计算完的结果也是一个无符号数。
举例来说:
(unsigned int)a / (signed int)b
(unsigned int)a / (unsigned int)b
计算结果也是一个无符号数。
再进一步,对于运算-2 / -1,如果采用有符号数运算,结果是1,采用无符号数运算,结果则是0。
所以
除法,取模这样的操作符在不同的上下文语境里对应的语义动作也有所不同,而且这种差异还不
同于c++里的操作符重载在语言级别可见,而是要到更底层的汇编语言级别才可见,这多少就有
一些tricky,也容易诱使程序员犯错了。如果有机会我来设计一门语言,我想自己会尽量避免引
入这种tricky的东西的。
-------------------------------------------------转贴结束------------------------------------------------------
刚才看到这篇文章的时候,下面有一个人的回复我也看了下,或许有人有同样的问题,我一并拿过来好了。
回复内容:
不评论,只是转载文章前,请楼主尝试一下 signed int a=-20; unsigned int b=10; signed int c=a+b; 你打印看看c 是多少。 带符号没。
a
-20
--------规整--------->
4294967276
======
0xFFFFFFEC
+
+
+
b
10
---------规整-------->
10
======
0x0000000A0
||
||
||
c
-10
<----格式化为%d----
4294967286
=======
0xFFFFFFFC
估计那个人打印的时候使用了%d,而%d默认是按有符号int类型输出,可以看到二进制码得出结果为:-10,结果正确。通过这个运算,也可以看出,有符号无符号不影响加法。