标签: it | 分类: IT |
这样一个问题:
int a = -2; printf("%d,%d", a/4*4, a/4U*4);
打印出的是:0,-4
有符号数与无符号数做除法时,双方都转为无符号数再做除法;
-2在计算机中的存储形式为:0xff ff ff fe
作为无符号数除以4时,向右移2位(左边补零),中间结果为0x3f ff ff ff
之后乘以4时,向左移位(右边补零),结果为oxff ff ff fc
该数字表示有符号数-4,于是-4就被打印出来。
参见文章:如流,新一代智能工作平台
【转】 有关C语言中有符号/无符号数混合运算的小问题
2010年08月25日 星期三 18:51
转载自 yjpro
最终编辑 yjpro
这两天的工作需要涉及到对无符合数,有符号数作混合运算。 作了一些实验,发现自己写了这么多年的程序,以前对于符号数计算相关的理解居然仍存在一些盲点。 有符号数与无符号数混合运算可能有三种混合方式 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的东西的。
|