补码知识解惑:
育儿知识问答: 解答你的疑惑 #生活乐趣# #生活分享# #亲子生活互动# #育儿心得分享#
0. 引言
今天在看 Oracle Java Tutorial 有关Comparator比较器时,教程提醒了我们使用Comparator的一个注意事项,下面我们简要介绍这个注意事项,引入本文的主题。
首先介绍Comparator的compare(T o1, T o2)返回值:
1. < 0 则 o1 < o2
2. = 0 则 o1 = o2
3. > 0 则 o1 > 02
public class EmpSort { static final Comparator<Employee> SENIORITY_ORDER = new Comparator<Employee>() { public int compare(Employee e1, Employee e2) { return e2.hireDate().compareTo(e1.hireDate()); } }; // Employee database static final Collection<Employee> employees = ... ; public static void main(String[] args) { List<Employee> e = new ArrayList<Employee>(employees); Collections.sort(e, SENIORITY_ORDER); System.out.println(e); } } 1234567891011121314151617
上面这段代码的意思就是想使用Comparator给List<Employee>排序,且是按雇佣日期的先后顺序排序的。
现在我们想要按照雇佣的时间先后逆序排序呢?是否我们可以将其更改,添加一个负号就行了呢?
return -r1.hireDate().compareTo(r2.hireDate(); 1
教程给出的警告是:不能!。
你应该使用前一种方法来排序,而不应该使用后面那种,因为后面的那种不能保证总是起作用。因为compareTo函数也就是r1.hireDate().compareTo(r2.hireDate()能够返回任意的负数int。
而接下来它给出的原因就是我们本文的重点!!了:
教程指出:如果仅仅是通过取负(取相反数,或者说添加一个负号)来修改Comparator是错误的因为Integer.MIN_VALUE取负,仍然为它本身,即下面条件是返回true的:
-Integer.MIN_VALUE == Integer.MIN_VALUE
而这与compare的返回值定义不一致,所以比较器函数并不完全正确
下面我们的问题就是解释-Integer.MIN_VALUE == Integer.MIN_VALUE;为何返回true
1. 补码
先,补码了解一下!
几乎所有计算机中表示有符号数,用的是补码。
补码的定义:
计算机中用 补码 表示的 位数为w 的二进制数,我们用向量X来表示:
X ⃗ \vec X X
= [ xw-1 xw-2 xw-3 … x2x1x0 ]
其在在现实生活中的 数字表示的 语义计算公式为:
-xw-12w-1 + ∑ i = 0 w − 2 x i 2 i \sum\limits_{i=0}^{w-2}{x_i}{2^i} i=0∑w−2xi2i
看起来这公式很难看懂?没关系,下面通过例子来总结其计算规律,很容易计算。
左边的数字为计算机中的补码,右边为补码在现实生活中表示的值:
[ 0001 ] -> -0·(2^3) + 0·(2^2) + 0·(2^1) + 1·(2^0) = 0+0+0+1 = 1
[ 0101 ] -> -0·(2^3) + 1·(2^2) + 0·(2^1) + 1·(2^0) = 0+4+0+1 = 5
[ 1011 ] -> -1·(2^3) + 0·(2^2) + 1·(2^1) + 1·(2^0) = -8+0+2+1 = -5
[ 1111 ] -> -1·(2^3) + 1·(2^2) + 1·(2^1) + 1·(2^0) = -8+4+2+2 = -1
最高位,我们解释为负权(negative weight)。这个名词很好理解。计算时所有的位我们按照常规的方法来计算,只不过最高位计算式,我们需要添加一个负号。
上面是一种计算方法,我们还有一种计算方法,本质是一样的:
1. 首先看最高位,如果为0,则表示正数,为1,表示负数。
2. 如果是正数,直接将补码的2进制转化为10进制就是我们所求。如果是负数,看第3步
3. 从最低位开始找第一个为1的位,此位之后的所有位取反,最终得到的2进制转化为10进制再×(-1)得到相反数即为最终的负数
下面我们仍然以上面的4个2进制数为例子:
[ 0001 ] -> [0001] = 0+0+0+1 = 1 // 正数直接求即可
[ 0101 ] -> [0101] = 0+4+0+1 = 5 // 正数直接求即可
// 第1个为1的位就是最低位,所以除了最低位,其它位都取反,并所得数的相反数
[ 1011 ] -> -[0101] = -(4+1) = -5
[ 1111 ] -> -[0001]= -1 // 同理
现在我们查看Java中关于Integer.MIN_VALUE定义:
@Native public static final int MIN_VALUE = 0x80000000; 1
Integer.MIN_VALUE值为0x80000000;
我们现在计算一下-MIN_VALUE——求二进制的相反数的方法为:各位取反再+1
0x80000000 -----(各位取反)-----> 0x7FFF FFFF -----(+1)----> 0x80000000
所以MIN_VALUE == -MIN_VALUE返回true
也可以这样求证:
令程序右边的 MIN_VALUE = X。
则MIN_VALUE + X = 0。
怎么求X?
因为MIN_VALUE + X = 0两个二进制数相加等于0必定是进位了。
我们可以通过这样的方式来计算X : X = 0x1_0000_0000 - MIN_VALUE
算出的X为0x80000000,即MIN_VALUE == X
也就证明了-Integer.MIN_VALUE == Integer.MIN_VALUE;
2. 参考文献
深入理解计算机系统(v3) P45网址:补码知识解惑: https://www.yuejiaxmz.com/news/view/358495
相关内容
保险理财知识视频——解锁财富增长的新密码数码常识
居家无聊不知如何打发时间,求解迷茫困惑有何良策?
幸福密码知识文章
什么是新能源汽车?新能源汽车有哪些?新能源汽车知识解码
有点用!数码小知识大盘点,快来一起了解!
数码小知识大盘点
一扫全知晓!安徽财政惠民惠农补贴二维码来了~
情感问答|你的困惑我们来解决
生活常识课让每个人的“小困惑”被看见