这一篇探讨的是“负数位运算的右移操作”,涉及到数据的源码、反码、补码的转换操作。属于C语言基础篇。

先看例子

#include

int main(void) {

//正数的位右移

//补码0000 0101

int x = +5;

//正数补码右移两位后

//补码0000 0001

printf("+5>>2 = %d\n", x>>2); //+5>>2 = 1

//负数的位右移

//补码1111 1011

int y = -5;

//负数补码右移两位后

//补码1111 1110

printf("-5>>2 = %d\n", y>>2); //-5>>2 = -2

}

输出结果。

好了,现在来解释一下这个输出结果是怎么来的。

在讨论负数的右移之前,我们先要了解一下什么是原码、什么是反码、什么是补码。

任何一个数据都有其唯一对应的原码、反码以及补码。计算机对于数据的处理都是以补码形式来进行的(至于为什么要这样就又可以展开一整篇文章来说明了,这里就不深入讨论了)。而且正数和负数的原反补码的转换规则是不一样的。

对于正数来说,其原、反、补码都是相同的,都是该数据的二进制形式。例如+5的原、反补码均为0000……0101(其中0的个数由该数据的类型以及计算机操作系统的位数决定)。其最高位为0表示正数。

对于一个有符号数(既不加unsigned修饰的数据类型)来说,其最高位便是它的符号位。由于有符号数的最高位为符号位,这就使得char类型有符号数的取值范围为-128~+127而不是0~256。

对于负数来说,其原码是在其正数原码的基础上,最高位改为1以表示负数。所以-5的原码为1000……0101。负数的反码是在保持源码符号位不变的情况下其余位取反。而负数的补码是其反码加1。

现在,我们知道了原码、反码、补码都是些什么了,那么这个例子的结果就很好分析了。

首先是 +5>>2 = 1

+5

原码 0000 …… 0101

补码 0000 …… 0101

>>2(正数右移高位补0)

补码 0000 …… 0001

原码 0000 …… 0001

= 1

然后是 -5>>2 = -2

-5

原码 1000 …… 0101

反码 1111 …… 1010 负数的反码是保留符号位不变源码取反

补码 1111 …… 1011 补码是反码加1

>>2 (负数右移高位补1)

补码 1111 …… 1110

反码 1111 …… 1101 补码转反码减1

源码 1000 … 0010 负数反码转源码保留符号位不变取反

= -2

以上就是有符号数的右移操作了,随便说一下“有符号数的左移”以及“负数的无符号右移”(没有“无符号左移”这个说法,因为左移是在低位补0,而符号位在高位,左移之后补的数据不能影响最终的符号)

有符号数的左移

无论是有符号的正数还是负数,其左移都是在其补码的基础上面左移,而且低位都是补0。看到这里,你是否就意识到了一个问题,“如果正数的补码在左移的过程中,刚好有一个1移到了最高位,那么是否就会变成了负数呢?”嗯,这种情况确实是会发生的。负数左移到一定值的时候也会变成正。

无符号右移

注意:在C语言中是没有“无符号右移”运算符的,在Java中用“>>>”表示,C语言中可以利用“((unsigned int)(-5))>>n”来实现

无论是正数还是负数,其无符号右移都是在其补码的基础上右移,高位补0。

例如

-5

原码 1000 …… 0101

反码 1111 …… 1010 负数的反码是保留符号位不变源码取反

补码 1111 …… 1011 补码是反码加1

>>>2(无符号右移,高位补0)

补码 0011 …… 1110

反码 (此时符号位已经变为0了,系统会当成正数来处理,原反补码均一样)

原码 0011 …… 1110

= 这个不好说,得看操作系统的位数(在我这里int为32位,

结果为:107374182==>00111111 11111111 11111111 11111110)

题外话(纯属瞎扯,感兴趣的可以看下)

为什么我们要讨论数据的移位操作呢?关于这点,我想要说一些不太恰当的题外话。

虽然对于嵌入式的开发,伦理上来说应该会经常涉及对于数据的位操作才对。曾经有个老师是这么对我说的“学单片机其实就是在学位操作”,但是我对于这句话的解读却不是太赞成,我觉得应该这么说才更符合现在的开发环境,“学单片机其实就是在学习控制位操作。”为什么这么说呢?我不太严谨的说一下自己的看法吧!虽然,移位操作在AT89S51的开发中表现的很明显。但是如今AT89S51的应用场景已经越来越少了。移位操作在STM32的开发中就表现的不太明显,由于在STM32的开发过程中,我们大多数时候都是用库函数来完成,利用了别人已经封装好的函数来开发。而且这些库函数大多数情况下还不仅仅是封装一层。这虽然大大提高了程序员开发时的方便性,不过也造成了初学者对于自己写的程序是如何指导芯片正常工作的中间过程模糊不清。如果你不用库函数来操作,效率又太低,所以大多数开发人员都还是用现成的库函数来开发。移位操作用的较少。

移位操作在底层的开发中(特别是汇编语言)用的很多,但在在应用层上面应用的比较少了。要求的掌握程度几乎处于“知道有这么一回事,接触到的时候,能想的起来知道它干了什么就行” 的地步。很多开发人员就只在刚刚开始学习编程的时候以及去面试做笔试的时候接触过数据的移位操作,之后就再也用不上了。但是对于类似数据移位的这种应用范围比较窄的知识点,我想要说的是,希望大家在学习的过程中多留一个心眼,不要觉得不重要不常用就不去重视。因为这些知识点往往能在某些特定场景下面有奇效。就例如移位操作的妙用。

在介绍这个妙用之前,我们需要知道一个前提。

计算机在处理数据的时候,处理加、减、乘、除所需要的时间是不一样的,其中加减所需要的时间和移位操作几乎是一样的可以忽略不计,但是乘法需要的时间却是加法的十到二十倍。而除法所需要的时间几乎是加法的二十到三十倍。具体是多少,这个不好说,在不同的机器上面是不一样的。但可以确定的是,乘除所需要的时间总是比加减移位多。特别是除法。

在刚刚开始接触数据的移位操作这个知识点的时候,老师就已经和我们说过移位操作的结果和原数据之间的关系。

其关系大概如下:

对于正数在不溢出的情况下的左移和右移(由于负数的左移和右移后得到的数据和原数据之间的关系不明显,所以仅仅讨论正数)

左移:

左移n位后的数据 = 原数据乘2n

右移:

右移n位后的数据 = 原数据除2n

一开始了解到这个知识点的时候,我对此是很不屑的,因为实在是太局限了,即便移位操作比乘除快很多,但是这个乘的数值或除的数值必须是2^n也太鸡肋了吧。而且如今计算机的运算速度这么快,这点运输速度之间的差异实在不算什么。

但是在后来我看的一个例子中,利用移位操作却有奇效。在这个例子中,有两个关键因素使得移位操作比乘除运算好。一个是对于运算结果精度要求不高,另外一个是运算的数据量巨大。这个例子展开来说,又是一整篇文章了。感兴趣的话可以看一下这篇文章。虽然在这个例子中,移位操作并不能真正解决最终问题。但是,却可以给我们一个启发“在某些场景下,一些平时不受待见的冷门知识点,却出奇的会很好用”。

原博客始发于CSDN,在如今博客界的转载抄袭泛滥的环境下,原创不易,点个赞再走呗。以下是博客首页的链接。

c语言位运算负数的实例_负数位运算的右移操作-C语言基础相关推荐

  1. c语言位运算负数的实例_巧妙运用C语言位运算

    原标题:巧妙运用C语言位运算 位运算 位运算的运算分量只能是整型或字符型数据,位运算把运算对象看作是由二进位组成的位串信息,按位完成指定的运算,得到位串信息的结果. 位运算符有: &(按位与) ...

  2. java求负数取模_负数参与取模运算

    学习Python看到数值运算这部分,看到取模运算,原来不仅正数负数都可以取模,浮点数,甚至复数都可以取模: 对于x%y, 如果都是整数,则返回x/y的余数: 如果是浮点数,返回的是x - int(x/ ...

  3. java 负数存储结构_负数在java中的存储和读取过程 | 学步园

    问题描述: 将-5存储在文本文件中,再读取出来显示到控制台; 预备知识: 1.在java中使用补码处理数字,而且byte(8)的数字在扩展成int(32)类型的时候,正数填充0,负数填充1; 2.负数 ...

  4. c语言位运算负数的实例_一招教你学会C语言中位运算

    程序中的所有数在计算机内存中都是以二进制的形式储存的.位运算说穿了,就是直接对整数在内存中的二进制位进行操作.注意,位运算只针对于整数进行操作. 运算符号 运算规则 1.&与运算:对应两个二进 ...

  5. c语言中的运算符及其含义_按位运算符及其在C语言中与Example一起使用

    c语言中的运算符及其含义 1)&(按位与) (1) & (bitwise AND)) It does AND on every bit of two numbers. The resu ...

  6. python负数取模_负数的取模运算

    我们知道,在不同的语言中,对负数执行取模运算,结果有可能会是不同的.例如,(-11)%5在python中计算的结果是4,而在C(C99)中计算的结果则是-1. truncate除法 && ...

  7. python负数求余数_负数求余数 C 和 MatlabPython 处理不一样

    在百度看到这个问题:http://zhidao.baidu.com/question/937487369300959012.html?oldq=1 用matlab和Python测试了下,发现都是这样: ...

  8. java负数转换为二进制_负数与二进制换转方法

    1.十进制负数转二进制 假设有一个 int 类型的数,值为5,那么,我们知道它在计算机中表示为: (因为java中int 是4个字节,所以高位需要补0,占够32位) 00000000 00000000 ...

  9. 幼儿园语言活动包括哪几类_幼儿园教育:《一起玩》语言活动教案

    活动目标: 1.喜欢安静倾听故事: 2.分段理解整个故事内容,感受分享玩具的乐趣: 3.能在大众面前大胆讲述故事. 活动准备:道具数个 活动过程: 一).活动导入: 教师:小朋友们大家上午好,今天小宇 ...

最新文章

  1. Mybatis解析动态sql原理分析
  2. MySQL-MMM架构部署(有图)
  3. TensorFlow(八)激活函数
  4. Hibernate事实:始终检查Criteria API SQL查询
  5. Ubuntu 16.04的k8s安装配置
  6. 肌电|表面肌电应用的新进展
  7. 开源究竟差哪了--- 关于开源软件和自由软件的区别
  8. 【转】SD-WAN,到底是什么*****
  9. SVD专题1 算子的奇异值分解——矩阵形式的推导
  10. js删除数组,checkedBox选中状态,javascript数组删除重复项
  11. ldc java_DMD与GDC与LDC的比较
  12. UE4 坐标系 坐标轴旋转轴
  13. Jboot通过redis实现每日登录失败次数限制的问题
  14. ERROR: Rosdep experienced an error: Unable to handle package.xml format version ‘3‘
  15. 广度优先搜索(BFS)最短路径输出表示(三种方法)
  16. 优化算法之引力搜索算法
  17. 星际争霸兵种的诉苦(简略版)
  18. 7-2 二叉搜索树的删除操作
  19. 国外问卷调查这个项目可以做吗?国外问卷调查怎么赚钱?
  20. 学习python[:,:]

热门文章

  1. ssh连接远程服务器报错:ssh_exchange_identification: read: Connection reset by peer
  2. 查看本地IP和服务器端口
  3. 报Keystore was tampered with, or password was incorret的原因
  4. 已解决Resource stopwords not found. Please use the NLTK Downloader to obtain the resource:
  5. sql 修改时间正价 2天_“熬夜3天,修改17遍,我终于写出了1篇稿费2万的稿子”...
  6. 人工智能真的要取代人类了?
  7. 一. 英语语法 - 简单句
  8. uniapp地图轨迹回放
  9. dig的现在分词_现在分词的变化规则(含双写加ing的动词归纳)
  10. NLP标注工具:brat【可用于标注:实体、关系、事件、属性】【只能用于Linux下】【开源、免费】