java中原码、反码和补码--时钟理解法

2024-01-26 15:10

本文主要是介绍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中原码、反码和补码--时钟理解法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/647204

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作