负数位运算的右移操作-C语言基础

这一篇探讨的是“负数位运算的右移操作”,涉及到数据的源码、反码、补码的转换操作。属于C语言基础篇。
先看例子
#include <stdio.h>
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语言基础的更多相关文章
- Sword 位运算取余操作
/* 位运算取余操作 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include ...
- 位运算 - 左移右移运算符 >>, <<, >>>
1-左移运算符m<<n,表示把m左移n位.左移n位的时候,最左边的n位数将被丢弃,同时在最右边补上n个0.例如: 00001010<<2 = 00101000 10001010 ...
- C#位运算实际作用之操作整型某一位
1.前言 前几天写了两篇关于c#位运算的文章 c#位运算基本概念与计算过程 C#位运算实际运用 在文中也提到了位运算的实际作用之一就是合并整型,当时引用了一个问题: C# 用两个short,一个int ...
- 面试必备:高频算法题终章「图文解析 + 范例代码」之 矩阵 二进制 + 位运算 + LRU 合集
Attention 秋招接近尾声,我总结了 牛客.WanAndroid 上,有关笔试面经的帖子中出现的算法题,结合往年考题写了这一系列文章,所有文章均与 LeetCode 进行核对.测试.欢迎食用 本 ...
- Java二进制和位运算,这一万字准能喂饱你
基础不牢,地动山摇.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习.关注公众号[BAT的乌托 ...
- Java中的二进制及基本的位运算
Java中的二进制及基本的位运算 二进制是计算技术中广泛采用的一种数制.二进制数据是用0和1两个数码来表示的数.它的基数为2,进位规则是"逢二进一",借位规则是"借一当二 ...
- Java位运算实现加减乘除
一.加法 a+b 举例实现:13+9=22 13+9不考虑进位结果为12 只考虑进位结果为10 和刚好是22. 13二进制为1101,9二进制为1001. 不考虑进位结果为0100.算式为a^b 只考 ...
- 《Thinking in Java》位运算
按位操作符: 首先先记住一件事,方便理解:是否对应正负对应10. 1.与(&):11得1,10得0,00得0. 2.或(|):11得1,10得1,00得0. 3.异或(^):11得0,10得1 ...
- Vus the Cossack and Strings(Codeforces Round #571 (Div. 2))(大佬的位运算实在是太强了!)
C. Vus the Cossack and Strings Vus the Cossack has two binary strings, that is, strings that consist ...
- day02<Java语言基础+>
Java语言基础(常量的概述和使用) Java语言基础(进制概述和二,八,十六进制图解) Java语言基础(不同进制数据的表现形式) Java语言基础(任意进制到十进制的转换图解) Java语言基础( ...
随机推荐
- VSCode-WSL配置 C++ —— 以OpenCV4为例
生成并编辑c_cpp_properties.json 命令窗口输入:>C/C++: Edit Configurations(JSON),就会自动生成该文件 在includePath中加上需要in ...
- list.size() = 1 但显示 All elements are null
https://blog.csdn.net/weixin_43899069/article/details/124668722 if (CollectionUtils.isNotEmpty(list) ...
- [*]Quadratic Residual Networks: A New Class of Neural Networks for Solving Forward and Inverse Problems in Physics Involving PDEs
Accepted by SIAM International Conference on Data Mining (SDM21) 本文提出了二次残差网络,通过在应用激活函数之前,添加二次残差项到输入的 ...
- 修改mysql多个表的相同字段为同一值内容
mysql将所有数据库的表的相同字段更新为某一值 1.创建存储过程函数名为:proc_update_client_id CREATE PROCEDURE `proc_update_client_id` ...
- 金蝶K3无法查看关联信息
场景: 某个用户点击采购订单界面--关联信息,界面显示正在加载,但是无法显示所有关联单据. 步骤: 1. 在其他电脑登录存在同样问题. 2. 其他模块可以正常显示 3. 删除该用户t_UserProf ...
- 把VScode插件提示abc的提示给移到最后
把VScode插件提示abc的提示给移到最后 解决方法 打开设置,在搜索中输入editor.snippetSuggestions,然后将选项改为top,就可以解决了 top:就是将你插件提示放到最上面 ...
- Mysql数据库基础第二章:(十)联合查询
Mysql数据库基础系列 软件下载地址 提取码:7v7u 数据下载地址 提取码:e6p9 mysql数据库基础第一章:(一)数据库基本概念 mysql数据库基础第一章:(二)mysql环境搭建 mys ...
- linux 部署python 系统服务管理命令 yum源设置 linux定时任务 python在linux的虚拟环境安装以及使用
安装python3 三种方式 ==linux下很多脚本默认都用python2, 所以不要把python3的执行文件改为python,因为linux里默认python就是运行python2版本 == y ...
- AD笔记总结
原理图要点: 1.新建工程, 新建的project是要以后缀PrjPCB2.新建原理图.PCB, 右键project,进行New添加3.新建原理图库.PCB库,右键project, 进行new添加 改 ...
- 2022-04-14内部群每日三题-清辉PMP
1.项目经理资源有限,无法获得更多资源.项目经理应该使用什么技术来充分利用现有资源,而不会令项目完成时间延期? A.资源平滑 B.资源平衡 C.快速跟进 D.赶工 2.正在审查问题日志的项目经理注意到 ...