本文主要是介绍java中原码、反码和补码--时钟理解法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
若想练得上乘功夫,必先基础功扎实。就像现在练吉他一样,想把和弦转换地流畅就得先把左手的按钮练好,否则可能连练下去的信心都没有了,或者勉强去转换和弦得到的音色肯定也不会理想,或者断断续续。虽然,从大学开始就接触java了,但是基本功一直不怎么扎实。以至于对现在的框架、设计模型等等理解的都比较浅。因此,我打算从新拾起这些基础知识好些研究下。因此,推出我个人博客的这个系列《静下心来学java》。
1、原码
第一位为符号位,余下的表示数的绝对值。
以byte类型举例:
0000 0001 表示+1
1000 0001 表示-1
2、反码
反码要分两种情况讨论。
正数的反码和原码相同。
负数的反码在原码的基础上,保持符号位不变,其他位取反。
以byte类型举例:
0000 0001的反码是0000 0001
1000 0001的反码是1111 1110
3、补码
同样,补码也分两种情况讨论。
正数的补码和原码相同。
负数的补码在其反码的基础上+1。
以byte类型举例:
0000 0001的补码是 0000 0001
1000 0001的补码是 1111 1111
上面只是对原码、反码和补码三者概念的介绍。那么,计算机中为什么要设计补码这一概念呢?直接用原码涉及到减法操作,这增加了算机底层电路涉及的复杂性。而用补码操作时,我们减去一个数时,可以看做加上一个负数,然后转变为加上这个负数的补码。
因此,现在我们只要知道一件事情,那就是计算机中没有减法操作,所有的减法操作都被转变为加法操作。下面,以一个时钟问题来探究补码的意义:
正如,我们上面看到的这个时钟这样,它的周期是12,这里我们把12看做0。-1点表示11点、-2点表示10点、以此类推,-11点表示一点。
对于这个时钟的该怎么看,有下面几点:
一、时钟上的可见性表示我们运算的数的范围,即0-11。
二、在时钟上的运算操作包括两种方式:往回拨(用一个负数表示),往前拨(用一个正数表示)。
下面通过时钟的操作来理解第一个问题–减少底层电路设计的复杂性。
为了实现时钟的运算,即往回拨和往前拨。第一种,用普通的方式(就相当于在计算机中用原码进行操作),我们得在时钟上安装两个拨片,一个向前拨动指针,一个向后拨动指针。
比如:+1操作,就需要用前拨拨片向前拨动一格。-2操作,需要后拨拨片向后拨动2格。
为了简化时钟设计的复杂性,我们决定只保留时钟上的一个前拨拨片,即指针只能被向前拨。这样的话,当进行+1操作的时候不变,向前拨一个。那么,-2操作该如何实现呢?上面我们提到-2表示10(这里10相当于-2的补码),那么-2操作就可以转变为向前拨动10格。比如,现在时间是6点整,那么-2应该是4点整。现在往前拨10格后,发现确实还是4点整。
哇!这样一来后拨拨片就完全被取代了(减法操作取代了),我们只需要一个前拨拨片即可(只需要加法操作)。
通过时钟,我们要明白周期这个东西,为什么-2可以用10来代替呢?因为这里的周期是12, -2+12 = 10。因此,-2的补码可以用+10表示。如同这个道理一样,所有的负数都可以用该周期内对应的一个正数来表示。而这个正数b的计算公式为:
c (周期)
a (周期内的一个负数)
b (a在c周期内对应的正数)
b = a+c
下面结合时钟问题,分析下java中正负数的加减问题。在java中,最小的整数类型是byte类型。一个字节,8位。表示的数的范围是-128~127。
为了方便分析,我现在假设有一种更小的数据类型只有4位,那么它的二进制的范围是0000~1111。由于第一位即高位是用来表示符号位的,那么表示正数的只能是0000~0111。换算为10进制表示的范围是0~7,共8个数字。4位二进制总共能表示16位数字,那么剩下的二进制就只能表示-1~-8了。
下面我们来试着用原码的方式来标注下-8~+7这几个数,看看会有什么意想不到的事情发生呢。
原码 反码 补码
-8
-7 1111
-6 1110
-5 1101
-4 1100
-3 1011
-2 1010
-1 1001
0 (1|0)000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
果然,是有意想不到的事情发生。-8并没有一种原码能表示,而0恰好有两种表示方法+0(0000)和-0(1000)。因此,当用原码来表示负数时,真是穷的穷死(-8没有二进制来表示),富的富的流油(0有两种表示方式)。
下面,我们继续把他们的反码和补码写出来:
原码 反码 补码
-8
-7 1111 1000 1001
-6 1110 1001 1010
-5 1101 1010 1011
-4 1100 1011 1100
-3 1011 1100 1101
-2 1010 1101 1110
-1 1001 1110 1111
0 (1|0)000 (1|0)111 (1|0)000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
正数的反码不补码是相同,就不写出来了。看到这里,你们可以在脑海中脑补一个0~15的时钟,那么这个时钟的周期就是16。只不过这个时钟的可见性是-8~7,因此它在钟表上的表示是这样的-8来代替指针上的8,用-7来代替9,因此类推用-1代替15。因此得到的钟表如下所示:
另外,根据补码的计算公式,-8的补码为-8+16=+8,二进制位1000。这真的巧了,刚好是0的两个补码中的一个,因此,我们将这个0多出来的补码交给-8。得到更新后的原码、反码、补码如下:
原码 反码 补码
-8 1000
-7 1111 1000 1001
-6 1110 1001 1010
-5 1101 1010 1011
-4 1100 1011 1100
-3 1011 1100 1101
-2 1010 1101 1110
-1 1001 1110 1111
0 (1|0)000 (1|0)111 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
虽然-8有补码了,但是它还是没有原码和反码。还有一点需要强调的是,指针的可见性,即数的表示范围-8~7。也就是说,我们计算出的结果的范围只能在这之间,如果超过的话,会被转化到这个范围之内,转换的公式。
a 待转换的数
c周期
b转换后的数
b = a%c
注意,在这里周期为16,被转化后b一定落在0~15之间,在8-15范围内的数字,如我的图所画的那样表示为对应的负数,也就是将补码转化为原码的过程。这个是因为,最高的以为被用来表示符号位了,所以有一半的二进制被用来表示负数了,所以最大值也由本来的15变为了7了。
因此,在手工所画的时钟里。7点整,在进行+2操作,得到的应该是9,但是9已经超出整数部分能表示的范围了,因此,这个9其实是-7的补码,它表示的是-7(原码)。
因此,我们可以这个扩展到byte字节来进行验证。byte所表示的整数的范围是-128~127。同样你可以在脑海中,脑补出一个周期为256的时钟画面,同样注意它的可见性,从128开始被-128代替,129被-127代替,一次类推,255被-1代替。
若在127点上进行+2操作,前面一格是-128,再前面一格是-127,你可以验证127+2得到的结果应该是-127。我们设计补码的作用还是为了进行减法操作。
例:
127点进行-3操作就相当于127加上一个-3在周期256范围内的补码是253。127+253 = 380。对380进行模运算380%256的结果为124, 124并没有超过最大的整数范围。因为结果位124。
根据上面的几种情况,我总结了java中正负数加法的规则:
1、计算机中没有减法操作,所有的减法都被替换为加法操作。
2、加上一个负数,这个负数可以表示为这个周期内对应的正数。
3、如果得到的结果是一个正数,且范围没有超过周期的大小,则不需要进行求模操作。如果没超过周期的范围,但是超过了最大整数的范围,那么这个结果其实是负数的补码,它的值应当是一个负数。
4、在二进制中,补码和补码的运算结果仍然是一个补码,以0开头的补码表示的是一个正数,以1开头的补码表示的是一个负数。补码转为原码的规则是先减1,然后取反。
补码设计原理
这篇关于java中原码、反码和补码--时钟理解法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!