做了这么多年软件开发,我发现一直没有搞懂有符号数,不知道你懂不懂?
问题是这样的,下位机程序往上位机发数据,发的是有符号数,上位机这边用字节流接收之后就按每两个字节转化为一个double类型的数据处理了,没有考虑符号位,也就是直接按无符号数处理了,导致发的和收的数据不一样。
趁此问题,肯定要好好研究一下有符号数和无符号数,以后再遇到此类问题就能避免不知不觉掉进坑里。
想理解有符号数、无符号数就需要先了解机器数、真值、原码、补码、反码这几个概念:
1000 0011
为例,其真正表示的值为-3,而形式值为131。0000 0011
和1000 0011
。0000 0011
和1111 1100
0000 0011
和1111 1101
从以上基本概念可以看出,正数的机器数、真值、原码、反码、补码都是一样的,而负数是不同的。
计算机中实际只存储补码。
所以如果是一个负数,就像我遇到的这个场景,下位机程序把一个负数发给上位机,上位机程序收到的实际是这个负数的补码,要得到其真实值就需要按有符号数来处理。我们就具体分析一下。
在我的案例中,下位机发送的数据是从-8192 ~ +8192范围内的整数,每个数用两个字节表示。
我们知道,两个字节,如果是无符号数,可以表示的范围是0 ~ 65535,如果是有符号数,可以表示的范围是-32768 ~ 32767
假设现在上位机收到的数据中有这样两个字节F7 AB
,对应的十进制是63403,显然是错误的,因为我不应该收到大于8192的数,那怎么把这个63403还原成实际的数值呢?
这里我们先写出一段可以实现这一还原过程的代码:
// 假设变量temp就是上面提到的63403,a是要得到的实际的的数值
if (temp > 32768)
{
int a = ((int)(temp - 1) ^ 0xFFFF) * -1;
}
解释如下:
既然这个数是个负数,那它的最高位一定是1,一个最高位是1的双字节数最小是1000 0000 0000 0000
,按无符号数去理解的话,对应的十进制数是32768,所以只要是大于32768的数就是要处理的数。
根据上面刚刚提到的基本概念,负数的补码是在其原码的基础上,符号位不变,其余各位取反后加1
,针对这句话做一个逆向操作:
temp - 1
FFFF
进行异或(^)操作,就反了过来。顺便把最高位的1符号位也干掉了,变成了0,这样它就不能参与真值的计算了经过计算,得到了实际值-2133。
以上只是一个思路,能实现的方法肯定不止这一种,还要根据实际情况具体分析。
他们说,生物的尽头是化学,化学的尽头是物理,物理的尽头是数学
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。