~0 == -1 问题全解

我们先看一个题目

#include<stdio.h>

int main()
{
	unsigned a,b,c,d;   //或者   int a,b,c,d;
	a=0x8;
	b=a>>1;
	c=~(~0<<1);
	d=b&c;
	printf("c is %d\n",c);
	printf("d is %d\n",d);
}

在这里插入图片描述
假设计算机是存储八位

0的存储是   0b 0000 0000
~按位取反   0b 1111 1111
左移一位    0b 1111 1110     空档处补0
再按位取反  0b 0000 0001

所以c就等于1

这里是一步一步推导过来的,你会忽略一个关键的过程,就是~0你算出来是
0b 1111 1111,一个很大的负数,这和你以为的常理违背,我们下面讨论。

再次举例

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

在这里插入图片描述

0的存储是   0b 0000 0000
~按位取反   0b 1111 1111

这个题目比较干脆,直接是~0 == -1,有的时候我们不明白,为什么0b 1111 1111在内存中代表-1,因为他无论如何也是一个很大的负数才对。

可是实际上,负数在内存中是按照补码的形式存储的,也就是说0b 1111 1111是一个补码,那么它的反码就是0b 1111 1110,原码就是0b 1000 0001,也就是-1(注意负数求反码补码的时候符号位不变)

所以结论

0b 1111 1111 == -1~00b 1111 1110 == -2~10b 1111 1101 == -3~2

可以把它当做一个公式 ~a == -【a+1】

补充说明

为什么整数要在内存中按照补码储存。因为正数的原码反码补码都一样,所以我们主要讨论问什么负数在内存中按照补码方式存储。

采用补码的原因或好处如下,采用补码运算具有如下两个特征:

1)因为使用补码可以将符号位和其他位统一处理,同时,减法也可以按加法来处理,即如果是补码表示的数,不管是加减法都直接用加法运算即可实现。

2)两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。

这样的运算有两个好处:

1)使符号位能与有效值部分一起参加运算,从而简化运算规则。从而可以简化运算器的结构,提高运算速度;(减法运算可以用加法运算表示出来。)

2)加法运算比减法运算更易于实现。使减法运算转换为加法运算,进一步简化计算机中运算器的线路设计。

用带符号位的原码进行乘除运算时结果正确,而在加减运算的时候就出现了问题,如下: 假设字长为8bits

( 1 ) - ( 1 ) = ( 1 ) + ( -1 ) = ( 0 )

(00000001)原 + (10000001)原 = (10000010)原 = ( -2 ) 显然不正确.。

因为在两个整数的加法运算中是没有问题的,于是就发现问题出现在带符号位的负数身上,对除符号位外的其余各位逐位取反就产生了反码。反码的取值空间和原码相同且一一对应。下面是反码的减法运算:

( 1 ) - ( 1 ) = ( 1 ) + ( -1 ) = ( 0 )

(00000001) 反+ (11111110)反 = (11111111)反 = ( -0 ) 有问题。

( 1 ) - ( 2) = ( 1 ) + ( -2 ) = ( -1 )

(00000001) 反+ (11111101)反 = (11111110)反 = ( -1 ) 正确

问题出现在(+0)和(-0)上,在人们的计算概念中零是没有正负之分的。

于是就引入了补码概念。负数的补码就是对反码加一,而正数不变,正数的原码反码补码是一样的。在补码中用(-128)代替了(-0),所以补码的表示范围为:

(-128~127)共256个。

注意:(-128)没有相对应的原码和反码, (-128) = (10000000) 补码的加减运算如下:

( 1 ) - ( 1 ) = ( 1 ) + ( -1 ) = ( 0 )

(00000001)补 + (11111111)补 = (00000000)补 = ( 0 ) 正确

( 1 ) - ( 2) = ( 1 ) + ( -2 ) = ( -1 )

(00000001) 补+ (11111110) 补= (11111111)补 = ( -1 ) 正确

采用补码表示还有另外一个原因,那就是为了防止0的机器数有两个编码。原码和反码表示的0有两种形式+0和-0,而我们知道,+0和-0是相同的。这样,8位的原码和反码表示的整数的范围就是-127-127(11111111-01111111),而采用补码表示的时候,00000000是+0,即0;10000000不再是-0,而是-128,这样,补码表示的数的范围就是-128~+127了,不但增加了一个数得表示范围,而且还保证了0编码的唯一性。

为什么正数的反码,补码和原码一样

这是规定 或者说这是约定,没有多少道理,你算是算不出来的。

补码只是为 负数 想出来的办法,目的是减法变加法。是减法可以 用 加补码 的方法实现。补码 可用反码加1得来。于是道又有了 负数的反码。

计算机里有硬件“加法器”,有了补码,减法 也可以 用 加法器 做了。计算机 里运算速度,硬件远快于软件。这是弄出反码,补码和原码花样的原因。

形象说明

引进补码的作用是为了让计算机更方便做减法
比如说,按时间12个小时来算
现在的准确时间是4点
有一个表显示的是7点
要校准时间,我们可以将时针退7-4=3格,也可以向前拨12-3=9格
计算机做减法就可以转化成-3=+9
这样可以简化计算机的硬件设备去做复杂的减法

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是一个简单的 MATLAB 最短路径问题的例题和答案全解问题描述: 给定一个图,其中已知各个节点之间的距离,求从节点 1 到节点 5 的最短路径。 图示: 2 3 / \ / \ 1 4 6 \ / \ / 5 7 解答: 首先定义邻接矩阵,表示各个节点之间的距离: distance = [ 0 2 inf inf inf inf inf; 2 0 3 inf inf inf inf; inf 3 0 inf 6 inf inf; inf inf inf 0 4 3 inf; inf inf 6 4 0 inf 5; inf inf inf 3 inf 0 5; inf inf inf inf 5 5 0 ]; 其中,0 表示节点到自身的距离为 0,inf 表示节点之间没有连接或不可达。 使用 Dijkstra 算法求解: 1. 初始化源节点和节点集: - 将源节点的距离设置为 0; - 将剩余节点的距离设置为 inf; - 将所有节点加入未访问集合。 nodes = [1 2 3 4 5 6 7]; %所有节点 start = 1; %源节点 L = inf(1,7); %距离 L(start) = 0; %源节点距离为 0 2. 进行循环,直到所有节点都被访问: - 从未访问集合中找到距离源节点最近的节点; - 更新其相邻节点的距离。 while ~isempty(nodes) %未访问节点不为空 [~,i] = min(L(nodes)); %距离最小的节点 node = nodes(i); %当前节点 nodes(i) = []; %将节点从未访问节点中删除 for j = find(distance(node,:)<inf) %相邻节点 L(j) = min(L(j),L(node)+distance(node,j)); %更新距离 end end 3. 输出从源节点到目标节点的最短路径: goal = 5; %目标节点 path = goal; %建立路径数组 while path(1) ~= start %路径搜索 for j = find(distance(path(1),:)<inf) %相邻节点 if L(j) == L(path(1))-distance(path(1),j) %路径上的距离 path = [j path]; %添加节点到路径 break end end end disp(['从节点 ',num2str(start),' 到节点 ',num2str(goal),' 的最短路径为:',num2str(path)]); disp(['距离为 ',num2str(L(goal))]); 输出结果: 从节点 1 到节点 5 的最短路径为:1 2 3 4 5 距离为 9 因此,从节点 1 到节点 5 的最短路径为 1-2-3-4-5,距离为 9。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式Linux系统开发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值