【位运算进阶之—-按位取反(~)】(附补码,原码讲解)

【位运算进阶之—-按位取反(~)】(附补码,原码讲解)原码是一种用二进制表示有符号整数的编码方式

大家好,欢迎来到IT知识分享网。

今天我们来谈谈按位取反这件事。
简单来说,按位取反就是先将一个数写成其二进制表达形式,然后1变0,0变1。下面就让我们展开深入地讨论吧!
在这里插入图片描述



一、预备知识:

1. 原码:

定义:

✨原码是一种用二进制表示有符号整数的编码方式。其中,最高位表示符号位0为正1为负其余位是数值位,表示数值的绝对值。

  • 举个“栗子”来说:
    • 1的原码是:001 ;
    • -1的原码是:101 ;

✨我们可以看出,互为相反数的两个数的原码,除了第一位的符号位不一样以外,其余位都相同。(那么,可能有聪明的小伙伴们就想到了0,是不是0也有两种表达方式呢?果然聪明!在原码中,0-0的表示是不一样的。)

优缺点:

  • ❤️优点:简单直观,易于理解和计算。
  • 💔缺点:加减法运算复杂,需要考虑符号位的处理。-0多占了一个数的表达位置,导致原码也可表示的总数减少了1个。

✨那可能又有小伙伴要问了,一个数很多吗?哈哈,一个-0单单看起来不多,但是一群-0不就多了吗?(这就告诉了我们”人多力量大”的道理,一个人可以走得很快,但一群人才能走得更远)。另外,-0的存在还会带来一些其他的问题:比如1️⃣(0)+(-0)=?2️⃣ 0和-0哪个大?

😢怎么一个原码事这么多?烦呐!下面就让我们来看看更为合适的编码方式–补码。


2. 补码:

❤️计算机中存储数据时,通常采用补码编码方式。

定义:

✨补码也是一种用于表示有符号整数的编码方式。其中,最高位(最左侧位)是符号位0表示正数,1表示负数其余位是数值位

  • 对于正数,补码就是其二进制原码本身。(看来不讲原码还不能开补码),如:数值+3的补码就是0011。
  • 对于负数,补码是其对应正数的原码的各位取反,末位加一,符号位取1。

优缺点:

  • ❤️优点:
    1️⃣加减法运算可以直接按位运算,无需特殊处理符号位。(相对原码来说更加方便)
    2️⃣解决了原码中存在的正零和负零的问题。(-0的位置让给了最小的数)
    3️⃣表示范围更加均衡。(补码的最小值的绝对值比最大值的绝对值多1)


  • 💔缺点:
    1️⃣加减法时可能溢出。
    2️⃣对称不明显。(看吧,上帝为你打开一扇窗的同时,也会为你关闭一扇门)

🐒当然,补码的知识宝库偌大无比,我现在只不过是揭开了其冰山一角,但对我们解决一般的问题来说,足够了。


二、按位取反:

✨将一个数的二进制表达的每一位取反,即0变为11变为0取反符号用 "~" 表示。(从最低位(最右侧位)开始,逐位进行取反操作。)

例如,对于一个8位的二进制数,按位取反后的结果是0。(注意,这是补码的取反哦)

#include<stdio.h> int main(void) { 
         int a = 0b1; printf("%d", ~a); return 0; } 
  1. 首先,a是一个int类型,一般是4个字节(Byte),而一个字节是8位(8个bit),所以总共是32位。0b1是1的二进制表达,具体可以写为:00000000 00000000 00000000 00000001.
  2. 下面我们对它进行取反操作,得: ,但它是一个补码,我们以%d的形式输出就要得到它的十进制表达(可以理解为真值),那么问题来了,补码怎么转换成真值呢?考虑到具体的定义过于枯燥乏味,所以我直接告诉你转换的方法:可以先将补码转换为机器数,而计算机中的机器数一般用原码来表示,所以问题就转化为了补码->原码
  3. 将补码的符号位不变,其余各位取反,末位加一。(这好像和原码转补码有点类似啊!)也就是: 00000000 00000000 00000001+1---> 00000000 00000000 00000010,再转换成十进制整数就是-2。

2️⃣0的按位取反:(%d的形式输出)

#include<stdio.h> int main(void) { 
         printf("%d", ~0); return 0; } 

在这里插入图片描述

2️⃣0的按位取反:(%u的形式输出)

#include<stdio.h> int main(void) { 
         printf("%u\n", ~0); printf("%lld", ((long long)1 << 32) - 1); return 0; } 

在这里插入图片描述
咦❓为啥同样是0,但取反后的输出结果不一样呢?

  • %d:有符号十进制整数的输出 (signed int): -2 ^ 31 -- 2 ^ 31 - 1
  • %u:无符号十进制整数的输出 (unsigned int) : 0 -- 2 ^ 32 - 1

这个有无符号具体就体现在是否有符号位,有符号位就少一位数值位,没有符号位就都是数值位。

  • 0的补码:00000000 00000000 00000000 00000000
  • ~0的补码:

三、拓展应用:

1. 巧求相反数:(不用-

✨相反数相必大家都知道,1的相反数是-1,0的相反数是0,简单来说一个数的相反数就是再其前面加个-

那么,不用这种方法还可以求相反数吗?

哈哈😄,其实这个问题我们上面已经说过了,直接利用补码的定义,求一个数的相反数,就是各位取反,末位加一。即-x=~x+1

#include<stdio.h> int main(void) { 
          int x = -18; printf("%d的相反数是%d\n",x, ~x + 1); int y = 18; printf("%d的相反数是%d",y, ~y + 1); return 0; } 

在这里插入图片描述
(当然了,要注意数据的溢出问题。)

思考:既然一个数x的相反数可以通过~x+1来得到,记为y

  • 则y=~x+1 -> ~(y-1)=x,所以,求x的相反数也可以用 ~(x-1)
#include<stdio.h> int main(void) { 
          int x = 18; printf("%d的相反数是%d\n", x, ~(x-1)); int y = 18; printf("%d的相反数是%d", y, ~y + 1); return 0; } 

2. 代替减法(不用-实现减法)

#include<stdio.h> int main(void) { 
           int a=1,b=2; printf("%d-%d=%d\n",a, b,a+~b+1); return 0; } 

在这里插入图片描述


3. 代替加法(不用+实现加法)

✨给定两个int类型的正数,不用+实现加法。根据加法定义,加上一个数等于减去一个数的相反数。即:a + b = a - (-b) = a - (~b +1)=a - ~b - 1;

#include<stdio.h> int main(void) { 
            int a=1,b=2; printf("%d+%d=%d\n",a, b,a-(~b + 1)); return 0; } 

在这里插入图片描述


在这里插入图片描述

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/112553.html

(0)
上一篇 2026-01-17 16:33
下一篇 2026-01-17 17:00

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信