笔记:无符号数与有符号数

读《深入理解计算机系统》第二章 信息的表示与处理

无符号数的编码就是简单的二进制编码,有符号数采用补码。

补码:

0001 = -23*0 + 22*0 + 21*0 + 20*1 = 1

1111 = -23*1 + 22*1 + 21*1 + 20*1 = -1

最高有效位(符号位)的权重是-2w-1,其中w是二进制长度

 

补码的非:

数字x的补码的非就是其加法逆元,即-x;其二进制表示为取反后再加1,即~x+1;计算机用加上逆元的方式来执行减法运算,底层并没有减法。

 

有符号与无符号数的运算:

有符号与无符号是编程语言层面的事情,是对二进制串的人为解读。底层CPU把一切都当作补码,用统一的规则进行运算,符号类型它是不关心的,无论运算数是无符号形式还是补码形式,都有一致的位级行为,这一点也是补码的优势。

  • 无符号减法

unsigned int a=1, b=2;

a-b > 0的结果为true。a、b不是补码形式,但在cpu看来2个操作数均是补码,用加法计算0x1+0xFFFFFFF2(2的逆元),计算结果(0xFFFFFFFF)则被程序解释为无符号数,所以大于0成立(根据c语言的规则,0被提升为无符号数后进行比较)。

  • 有符号减法

int a=1, b=2;

a-b > 0的结果为false。底层的位级运算与上面无符号例子一样,计算结果(0xFFFFFFFF)则被程序解读为有符号数-1,所以大于0不成立。

  •  混合运算

需要特别注意的是2种数的混合运算,因为c语言中规定同级别的无符号数的精度高于有符号数,所以会隐式地将有符号数转为无符号数(底层位级表示不变,只是解读方式不同)。

表达式-1 < 1U,第一个操作数会被隐式转为无符号数,等价于4294967295U < 0U,结果是false。

表达式-5-1U < 0,底层运算全按照补码规则进行,但结果被解读为无符号数后进行比较,所以是false。

混合运算很容易犯错,习题2.26中函数比较比较字符串长度,如下,代码中使用了无符号数与0做比较,结果往往不符合预期,应该改为strlen(s) > strlen(t)

size_t strlen(const char *s);

int strlonger(char*s, char* t){

    return strlen(s) - strlen(t) > 0;

}

  • 整数乘法

机器用一种指令来进行有符号与无符号整数的乘法,如果溢出返回的是截断后的结果。

  • 常整数声明

10进制整数常量的实际类型取决于其长度及后缀(ULL之类),在长度没有溢出的情况下,整数常量的值总是非负的,如果前面有个负号,它是应用于这个常量的单目操作符,本身不是常数的一部分。在我的环境上,

int型常量:[-2147483647, 2147483647]内的常量数字,外加最小int数用(-2147483647-1)表示。不能用-2147483648是因为2147483648超出了int的表示范围,在vc下会告警“warning C4146: 一元负运算符应用于无符号类型,结果仍为无符号类型”,所以C语言中最小int数的宏定义是#define INT_MIN (-INT_MAX-1)。

unsigned int常量:添加了U后缀的[0, 2147483647]内的常量数字,[2147483648, 4294967295]。

更大的数就被认为是long long了。另外,将[2147483648, 4294967295]内的常数赋值给int型变量,比如int i=2147483648,vc2008编译器不会告警,直接按照位模式复制,i是负数,很容易出错。

 

类型转换+位扩展:

short i = -12345;

unsigned u = i;

以上操作等价于(unsigned)(int)i,得到4294954951,而不是(unsigned)(unsigned short)i,后者求值为53191.

你可能感兴趣的:(笔记)