C语言之位运算/原码/反码/补码、进制间的转换、常规应用

2024-05-31 10:38

本文主要是介绍C语言之位运算/原码/反码/补码、进制间的转换、常规应用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

所谓位运算,就是对一个比特(Bit)位进行操作。比特(Bit)是一个电子元器件,8个比特构成一个字节(Byte),它已经是粒度最小的可操作单元了。

****进制间的转换:

 16进制转换成10进制:例如0xff:  15*16^1+15*16^0=255。

10进制转换成16进制:用十进制数整除以16,然后取余数,直到商为0则停止转换。余数可以是0~15中的某一个数,其中0~9不用改变,10~15则分别用A~F表示。最先得到的余数是最低位,最后得到的是最高位,由高到低排列得到16进制数(逆序)。(也可以先八10进制转换成二进制,再把二进制转换成16进制.)

 

二进制转成10进制:数字中所有位*本位的权重然后求和,注意如果二进制有小数部分,小数部分的权重指数是负的;例如10101.101=1*2^4+0*2^3+1*2^2+0*2^1+1*2^0+1*2^-1+0*2^-2+1*2-3=21.625,

10进制转换二进制:将10进制数字不断除以2直到商为零,然后将余数由下至上依次写出(逆序),即可得到该数字的二进制表示.

10进制小数转换成二进制:十进制小数转R进制小数(其他进制方法类似),方法为乘R取整,每次乘以相应之后基数后取结果的整数部分即可,直到小数部分为0,但是并非所有的十进制小数都能完全转化为R进制小数,这时就需要根据要求的精度值,类似四舍五入(比如二进制0舍去,1进位)。例如:

0.625*2=1.25  整数1

0.25*2=0.5      整数0

0.5*2=1.0         整数1

结果按顺序排列得到0.101;

 

二进制转换成八进制:从右向左,每三位一组(不足三位的在左侧补0),转换成8进制;

8进制转换成二进制:用3位二进制代码替换每一位8进制;

 

二进制转行成16进制:从右向左,每四位一组(不足四位的在左侧补0),转换成16进制;

16进制转换成二进制:用四位二进制代码替换每一位16进制;

 

负数的16进制的表示方法:首先应该将其表示成二进制形式,然后变反码,再变补码,然后把补码每四位转换成16进制。

以-1来说:将-1表示成4位二进制数(求补码),就是1111(此时将1111当成有符号数),然后直接化16进制数;-1的一位有符号16进制数就是 F.
-10的8位有符号二进制数为11110110(补码表示),化为2位有符号的16进制数FA.

如何判断一个16进制数是正是负?
看有没有指明这个16进制数是否为有符号数,如果题目说明为无符号数,则表示正数. 如果为有符号数,就要判断符号的正负:将16进制数的最高位化为4位二进制数,如果所化的二进制数的最高位为1就表示负数,为0就表示正数.
例 FA 为有符号的16进制数,F为FA的最高位,化为二进制数为1111,而1111的最高位为1,就表示FA是个负数.FA化为10进制数就为 -10

负数的二进制表示:最左侧位0表示整数,1表示负数;

溢出:https://www.cnblogs.com/Jamesjiang/p/8947252.html

 

***常规使用:

为什么要byte要和0xff进行按位与运算?

当一个byte会转换成int时,由于int是32位,而byte只有8位这时会进行补位,例如补码11111111的十进制数为-1,转换为int时变为11111111 11111111 11111111 11111111,即0xffffffff但是这个数是不对的,这种补位就会造成误差。和0xff相与后,高24比特就会被清0了,结果就对了。

 

C语言提供了六种位运算符:

运算符 & | ^ ~ << >>

说明 按位与 按位或 按位异或 取反 左移 右移

 

*****************按位与运算(&)

一个比特(Bit)位只有 0 和 1 两个取值,只有参与&运算的两个位都为 1 时,结果才为 1,否则为 0。例如1&1为 1,0&0为 0,1&0也为 0,这和逻辑运算符&&非常类似。

 

C语言中不能直接使用二进制,&两边的操作数可以是十进制、八进制、十六进制,它们在内存中最终都是以二进制形式存储,&就是对这些内存中的二进制位进行运算。其他的位运算符也是相同的道理。

 

例如,9 & 5可以转换成如下的运算:

0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

& 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-----------------------------------------------------------------------------------

0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001  (1 在内存中的存储)

也就是说,按位与运算会对参与运算的两个数的所有二进制位进行&运算,9 & 5的结果为 1。

 

又如,-9 & 5可以转换成如下的运算:

1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

& 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-----------------------------------------------------------------------------------

0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-9 & 5的结果是 5。

&是根据内存中的二进制位进行运算的,而不是数据的二进制形式;其他位运算符也一样。以-9&5为例,-9 的在内存中的存储和 -9 的二进制形式截然不同:

1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

-0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (-9 的二进制形式,前面多余的 0 可以抹掉)

 

按位与运算通常用来对某些位清 0,或者保留某些位。例如要把 n 的高 16 位清 0 ,保留低 16 位,可以进行n & 0XFFFF运算(0XFFFF 在内存中的存储形式为 0000 0000 -- 0000 0000 -- 1111 1111 -- 1111 1111)。

 

【实例】对上面的分析进行检验。

#include <stdio.h>

int main(){

    int n = 0X8FA6002D;

    printf("%d, %d, %X\n", 9 & 5, -9 & 5, n & 0XFFFF);

    return 0;

}

运行结果:

1, 5, 2D

 

 

**********按位或运算(|)

 

参与|运算的两个二进制位有一个为 1 时,结果就为 1,两个都为 0 时结果才为 0。例如1|1为1,0|0为0,1|0为1,这和逻辑运算中的||非常类似。

 

例如,9 | 5可以转换成如下的运算:

0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

|   0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-----------------------------------------------------------------------------------

0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1101  (13 在内存中的存储)

9 | 5的结果为 13。

 

又如,-9 | 5可以转换成如下的运算:

1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

|   0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-----------------------------------------------------------------------------------

1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

-9 | 5的结果是 -9。

 

按位或运算可以用来将某些位 置 为1,或者保留某些位。例如要把 n 的高 16 位置 1,保留低 16 位,可以进行n | 0XFFFF0000运算(0XFFFF0000 在内存中的存储形式为 1111 1111 -- 1111 1111 -- 0000 0000 -- 0000 0000)。

 

【实例】对上面的分析进行校验。

#include <stdio.h>

int main(){

    int n = 0X2D;

    printf("%d, %d, %X\n", 9 | 5, -9 | 5, n | 0XFFFF0000);

    return 0;

}

运行结果:

13, -9, FFFF002D

 

*********按位异或运算(^)

 

参与^运算两个二进制位不同时,结果为 1,相同时结果为 0。例如0^1为1,0^0为0,1^1为0。

 

例如,9 ^ 5可以转换成如下的运算:

0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

^  0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-----------------------------------------------------------------------------------

0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1100  (12 在内存中的存储)

9 ^ 5的结果为 12。

 

又如,-9 ^ 5可以转换成如下的运算:

1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

^  0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-----------------------------------------------------------------------------------

1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0010  (-14 在内存中的存储)

-9 ^ 5的结果是 -14。

 

按位异或运算可以用来将某些二进制位反转。例如要把 n 的高 16 位反转,保留低 16 位,可以进行n ^ 0XFFFF0000运算(0XFFFF0000 在内存中的存储形式为 1111 1111 -- 1111 1111 -- 0000 0000 -- 0000 0000)。

 

【实例】对上面的分析进行校验。

#include <stdio.h>

int main(){

    unsigned n = 0X0A07002D;

    printf("%d, %d, %X\n", 9 ^ 5, -9 ^ 5, n ^ 0XFFFF0000);

    return 0;

}

运行结果:

12, -14, F5F8002D

 

 

**************取反运算(~)

 

取反运算符~为单目运算符,右结合性,作用是对参与运算的二进制位取反。例如~1为0,~0为1,这和逻辑运算中的!非常类似。。

 

例如,~9可以转换为如下的运算:

~ 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

-----------------------------------------------------------------------------------

1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0110  (-10 在内存中的存储)

所以~9的结果为 -10。

 

例如,~-9可以转换为如下的运算:

~ 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

-----------------------------------------------------------------------------------

0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1000  (9 在内存中的存储)

所以~-9的结果为 8。

 

【实例】对上面的分析进行校验。

#include <stdio.h>

int main(){

    printf("%d, %d\n", ~9, ~-9 );

    return 0;

}

运行结果:

-10, 8

 

 

************左移运算(<<)

 

左移运算符<<用来把操作数的各个二进制位全部左移若干位,高位丢弃,低位补0。

 

例如,9<<3可以转换为如下的运算:

<< 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

-----------------------------------------------------------------------------------

0000 0000 -- 0000 0000 -- 0000 0000 -- 0100 1000  (72 在内存中的存储)

所以9<<3的结果为 72。

 

又如,(-9)<<3可以转换为如下的运算:

<< 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

-----------------------------------------------------------------------------------

1111 1111 -- 1111 1111 -- 1111 1111 -- 1011 1000  (-72 在内存中的存储)

所以(-9)<<3的结果为 -72

 

如果数据较小,被丢弃的高位不包含 1,那么左移 n 位相当于乘以 2 的 n 次方

 

【实例】对上面的结果进行校验。

#include <stdio.h>

int main(){

    printf("%d, %d\n", 9<<3, (-9)<<3 );

    return 0;

}

 

运行结果:

72, -72

 

************右移运算(>>)

 

右移运算符>>用来把操作数的各个二进制位全部右移若干位,低位丢弃,高位补 0 或 1。如果数据的最高位是 0,那么就补 0;如果最高位是 1,那么就补 1。

 

例如,9>>3可以转换为如下的运算:

>> 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

-----------------------------------------------------------------------------------

0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001  (1 在内存中的存储)

所以9>>3的结果为 1。

 

又如,(-9)>>3可以转换为如下的运算:

>> 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

-----------------------------------------------------------------------------------

1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 1110  (-2 在内存中的存储)

所以(-9)>>3的结果为 -2

 

如果被丢弃的低位不包含 1,那么右移 n 位相当于除以 2 的 n 次方(但被移除的位中经常会包含 1)。

 

【实例】对上面的结果进行校验。

#include <stdio.h>

int main(){

    printf("%d, %d\n", 9>>3, (-9)>>3 );

    return 0;

}

运行结果:

1, -2

 

=============原码、反码、补码:

 

一、什么是原码、反码和补码

  在计算机内部存储的带符号数都是以补码形式存储,用补码形式进行运算的。以整型数为例,且假定字长为8位。

  1、原码

  整数X的原码是指:其符号位为0表示正,为1表示负;其数值部分就是X的绝对值的二进制数。X的原码通常用【X】原表示。如:

  【+100】原=01100100 【+0】原=00000000

  【-100】原=11100100 【-0】原=10000000注意:在原码中,零有两种表示形式。

  原码表示法简单易懂,与真值(带符号数本身)转换方便,只要符号还原即可,但当两个正数相减或不同符号数相加时,必须比较两个数哪个绝对值大,才能决定谁减谁,才能确定结果是正还是负,所以原码不便于加减运算。

  2、反码

  X的反码是指:对于正数,反码与原码相同;对于负数,符号位不变,其数值位X的绝对值取反(1变0,0变1)。X的反码通常用【X】反来表示。如

  【+100】反=01100100 【+0】反=00000000

  【-100】反=10011011【-0】反=11111111

  注意:在反码中,零也有两种表示形式。

  反码运算也不方便,通常用来作为求补码的中间过渡。

  3、补码

  X的补码是指:对于正数,补码与原码相同;对于负数,符号位不变,其数值位X的绝对值取反后在最低位加1。X的补码通常用【X】补来表示,实际上,【X】补=【X】反+1。如:

  【+100】补=01100100 【+0】补=00000000

  【-100】补=10011100 【-0】补=00000000

  注意:在补码中,零有唯一的编码,【+0】补=【-0】补=00000000。

  补码运算简单方便,符号位可以作为数据的一位参与运算,不必单独处理;二进制的减法可用其补码的加法来实现,简化了硬件电路。

1.  负数原码和反码的相互转化负数原码转化为反码:符号位不变,数值位按位取反。如:原码 1100 0010
反码 1011 1101负数反码转化为原码:符号位不变,数值位按位取反。反码 1011 1101
原码 1100 00102.  负数原码和补码的相互转化负数原码转化为补码:符号位不变,数值位按位取反,末尾加一。原码 1100 0010
反码 1011 1101 //符号位不变,数值位按位取反
补码 1011 1110 //末尾加1负数补码转化为原码:符号位不变,数值位按位取反,末尾加1。补码 1011 11101100 0001 //符号位不变,数值位按位取反
原码 1100 0010 //末尾加13.负数反码和补码的相互转化负数反码转化为补码:末尾加1。反码 1011 1101
补码 1011 1110负数补码转化为反码:末尾减1(注意,此处的反码是指原码的反码)。补码         1011 1110
原码的反码   1011 1101
//减法 借位        

  补码转换为原码:符号位不变,数值位按位取反,末位再加1。即补码的补码等于原码.

一个汉子等于两个字节,包括中文标点符号;一个英文占一个字节;

两个16进制数等于一个字节;一个字节占8位,最多表示-128~127或者0~255;

很多计算,都使用0x这样的16进制进行运行:如 0xF的二进制为 1111 ,即四个1。0xFF的二进制为 1111 1111 ,即8个1的二进制形式每多一个F就是多一个4位的1111。最多8个F。

***16进制的反码和补码:

将一个十六进制整数按位取反并加 1,就生成了它的补码。一个简单的十六进制数字取反方法就是用 15 减去该数字。下面是一些十六进制数求补码的例子:

6A3D --> 95C2 + 1 --> 95C3
95C3 --> 6A3C + 1 --> 6A3D

通过检查十六进制数的最高有效(最高)位,就可以知道该数是正数还是负数。如果最高位 ≥ 8,该数是负数;如果最高位 ≤ 7,该数是正数。比如,十六进制数 8A20 是负数,而 7FD9 是正数.

 

这篇关于C语言之位运算/原码/反码/补码、进制间的转换、常规应用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1017758

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

zoj3820(树的直径的应用)

题意:在一颗树上找两个点,使得所有点到选择与其更近的一个点的距离的最大值最小。 思路:如果是选择一个点的话,那么点就是直径的中点。现在考虑两个点的情况,先求树的直径,再把直径最中间的边去掉,再求剩下的两个子树中直径的中点。 代码如下: #include <stdio.h>#include <string.h>#include <algorithm>#include <map>#

usaco 1.2 Palindromic Squares(进制转化)

考察进制转化 注意一些细节就可以了 直接上代码: /*ID: who jayLANG: C++TASK: palsquare*/#include<stdio.h>int x[20],xlen,y[20],ylen,B;void change(int n){int m;m=n;xlen=0;while(m){x[++xlen]=m%B;m/=B;}m=n*n;ylen=0;whi

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【区块链 + 人才服务】可信教育区块链治理系统 | FISCO BCOS应用案例

伴随着区块链技术的不断完善,其在教育信息化中的应用也在持续发展。利用区块链数据共识、不可篡改的特性, 将与教育相关的数据要素在区块链上进行存证确权,在确保数据可信的前提下,促进教育的公平、透明、开放,为教育教学质量提升赋能,实现教育数据的安全共享、高等教育体系的智慧治理。 可信教育区块链治理系统的顶层治理架构由教育部、高校、企业、学生等多方角色共同参与建设、维护,支撑教育资源共享、教学质量评估、

uva 10061 How many zero's and how many digits ?(不同进制阶乘末尾几个0)+poj 1401

题意是求在base进制下的 n!的结果有几位数,末尾有几个0。 想起刚开始的时候做的一道10进制下的n阶乘末尾有几个零,以及之前有做过的一道n阶乘的位数。 当时都是在10进制下的。 10进制下的做法是: 1. n阶位数:直接 lg(n!)就是得数的位数。 2. n阶末尾0的个数:由于2 * 5 将会在得数中以0的形式存在,所以计算2或者计算5,由于因子中出现5必然出现2,所以直接一