一.有符号数、无符号数
有符号数是针对二进制来讲的。用最高位作为符号位,“0"代表”+",“1"代表”-" ;其余数位用作数值位,代表数值。
有符号数的表示:计算机中的数据用二进制表示,数的符号也只能用0/1表示。一般用最高有效位(MSB)来表示数的符号,正数用0表示,负数用1表示。
无符号数计算:
有符号数(补码)计算:
有符号整数的表示方法主要有以下三种:
原码:(原码表示法中,最高位(符号位)用于表示整数的正负(0表示正数,1表示负数),其余位表示数值的大小。原码表示法的优点是直观易懂,但在进行算术运算时需要分情况讨论,使得计算过程变得复杂。
反码:反码表示法中,正数的反码与原码相同;负数的反码是保留符号位为1,然后对原码的数值位取反(0变为1,1变为0)。
补码:补码表示法是计算机中最常用的有符号整数表示方法。正数的补码与原码相同;负数的补码是保留符号位为1,然后对原码的数值位取反,最后加1。补码表示法的优点是加法运算简单且唯一,不需要区分正负数,而且没有重复的零表示。
无符号整数没有符号位,因此只能表示正数,全部位置都用来表示数值。
因此,8位有符号数整数所能表示的范围为-128到127,但是8位二进制无符号整数所能表示的范围是0~256。
补充:
为什么有些人的代码里会用到unsigned int?
unsigned int 是一种无符号整数类型,它只能表示非负整数。与有符号整数类型(例如 int)相比,unsigned int 有一些特点和用途:
范围:由于 unsigned int 不需要表示负数,它的值域是 0 到最大正数的两倍。这意味着它可以表示更大的整数。例如,在一个 32 位系统上,int 的范围是 -2,147,483,648 到 2,147,483,647,而 unsigned int 的范围是 0 到 4,294,967,295。
位运算:当你需要对整数进行位运算(如按位与、按位或、按位异或等)时,使用 unsigned int 可以避免一些与符号位相关的问题。由于 unsigned int 是无符号的,所以位运算更直观且不易出错。
用途:unsigned int 常用于表示计数器、索引、大小、哈希值等,这些场景下的值通常是非负的。使用 unsigned int 可以明确表示这些值应始终为非负。
溢出行为:unsigned int 的溢出行为是定义良好的,按照模数算术(模 2^n,n 为位数)进行。这意味着,当一个 unsigned int 值达到其最大值并继续增加时,它会回绕到 0。这在某些应用中可能是所需的行为。
然而,需要注意的是,unsigned int 也可能导致一些不易察觉的错误。例如,在涉及混合类型运算时,unsigned int 可能会导致意外的类型提升和隐式类型转换。因此,在使用 unsigned int 时,要确保正确处理这些可能的问题。
无符号数和有符号数之间的转化:
一.有符号数转化为无符号数
转化结果:1.编码本身没有发生变化,2.非负数没有发生变化,3.负数变成一个(大)正数
变化公式:
二.无符号数转化为有符号数
转化公式:
其中Tmax表示范围最大值。
补充:在一个表达式中混用有符号数和无符号数时,有符号数会被隐式转换为无符号数。
例子:
二.原码、补码、反码
,计算机中只由1和0表示,当然包括我们常见的表示负数的符号"-",规定:一个有符号二进制数,其最高位为符号位,1表示负,0表示正,剩余位才是数值域。所以一个字节(8位),如果无符号可以表示2^8个(其范围为:0~2^8-1)不同的数(在计算机中可能是对应2^8个不同的状态),但是如果是有符号数,则需要牺牲最高位来表示符号位,所以虽然同样可以表示2^8个数,但是表示范围成了:-2^7 ~ 2^7-1。
正数:原码、反码、补码都是其本身。
负数:原码=本身;反码=原码符号位不变,其它位取反。
补码:反码+1,例:10010001:原=10010001 反=11101110 补:11101111
由上面的关系其实可以看出来,如何求正码和补码之间的转换了。
原码和补码如何互相转换?
原码到补码的转换上面有写。如果是正数的话表示相同。如果是负数的话,补码到原码的转换,就是各个位取反(不对符号位进行操作),然后加一。
注:系统中二进制均以补码形式存在。
三.零扩展和补符号位扩展
符号扩展是计算机算术中在保留数字的符号(正/负)和值的同时增加二进制数的位数的操作。 这是通过根据所使用的特定带符号的数字表示的过程,将数字附加到数字的最高有效位来完成的。
例如,如果使用六位表示数字“ 00 1010”(十进制正数10),并且符号扩展操作将字长增加到16位,则新的表示形式就是“ 0000 0000 0000 1010”。因此,既保持了价值,又保持了价值为正的事实。
如果用10位表示用二进制补码值“1111110001”(十进制负15),并且将其符号扩展为16位,则新表示为“1111 1111 1111 0001 ”。因此,通过在左侧填充ones,可以保持负号和原始编号的值。 1111 1111 1111 0001。
举个例子:
-127原码1111 1111,反码1000 0000,补码1000 0001。计算机存储的是1000 0001,用十六进制表示为0x81。
当使用补零扩展时,结果为:
0000 0000 0000 0000 0000 0000 1000 0001 (与补码数值形式一致)
用十六进制表示为0x81。为了计算十进制值,计算它的补码,结果为:
0000 0000 0000 0000 0000 0000 1000 0001
将这个二进制数转成十进制的结果是129。
当使用补符号位扩展时,结果为:
1111 1111 1111 1111 1111 1111 1000 0001 (和补码数值看上去差别较大)
用十六进制表示为0xFFFFFF81。为了计算十进制值,计算它的补码,结果为:
1000 0000 0000 0000 0000 0000 0111 1111
将这个二进制数转成十进制的结果是-127。
由此可以得出结论:
(1)使用补零扩展能够保证二进制存储的一致性(和我们数学常理一致),但不能保证十进制值不变。所以,处理无符号二进制数的时候,可以使用零扩展(zero extension)将小位数的无符号数扩展到大位数的无符号数
(2)使用补符号位扩展能够保证十进制值不变,但不能保证二进制存储的一致性(负数的补码变了,需要 &0xff),而处理不同长度的有符号数时,我们必须使用符号扩展。
四.运算(找了点图片,耐心看完即可)
接下来的移位要打起精神看一下(写代码时经常会遇到):
最后小结一下:
至此,功德圆满!