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

相关文章

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

破茧 JDBC:MyBatis 在 Spring Boot 中的轻量实践指南

《破茧JDBC:MyBatis在SpringBoot中的轻量实践指南》MyBatis是持久层框架,简化JDBC开发,通过接口+XML/注解实现数据访问,动态代理生成实现类,支持增删改查及参数... 目录一、什么是 MyBATis二、 MyBatis 入门2.1、创建项目2.2、配置数据库连接字符串2.3、入

Springboot项目启动失败提示找不到dao类的解决

《Springboot项目启动失败提示找不到dao类的解决》SpringBoot启动失败,因ProductServiceImpl未正确注入ProductDao,原因:Dao未注册为Bean,解决:在启... 目录错误描述原因解决方法总结***************************APPLICA编

深度解析Spring Security 中的 SecurityFilterChain核心功能

《深度解析SpringSecurity中的SecurityFilterChain核心功能》SecurityFilterChain通过组件化配置、类型安全路径匹配、多链协同三大特性,重构了Spri... 目录Spring Security 中的SecurityFilterChain深度解析一、Security

SpringBoot多环境配置数据读取方式

《SpringBoot多环境配置数据读取方式》SpringBoot通过环境隔离机制,支持properties/yaml/yml多格式配置,结合@Value、Environment和@Configura... 目录一、多环境配置的核心思路二、3种配置文件格式详解2.1 properties格式(传统格式)1.

Apache Ignite 与 Spring Boot 集成详细指南

《ApacheIgnite与SpringBoot集成详细指南》ApacheIgnite官方指南详解如何通过SpringBootStarter扩展实现自动配置,支持厚/轻客户端模式,简化Ign... 目录 一、背景:为什么需要这个集成? 二、两种集成方式(对应两种客户端模型) 三、方式一:自动配置 Thick

Spring WebClient从入门到精通

《SpringWebClient从入门到精通》本文详解SpringWebClient非阻塞响应式特性及优势,涵盖核心API、实战应用与性能优化,对比RestTemplate,为微服务通信提供高效解决... 目录一、WebClient 概述1.1 为什么选择 WebClient?1.2 WebClient 与

Java.lang.InterruptedException被中止异常的原因及解决方案

《Java.lang.InterruptedException被中止异常的原因及解决方案》Java.lang.InterruptedException是线程被中断时抛出的异常,用于协作停止执行,常见于... 目录报错问题报错原因解决方法Java.lang.InterruptedException 是 Jav

深入浅出SpringBoot WebSocket构建实时应用全面指南

《深入浅出SpringBootWebSocket构建实时应用全面指南》WebSocket是一种在单个TCP连接上进行全双工通信的协议,这篇文章主要为大家详细介绍了SpringBoot如何集成WebS... 目录前言为什么需要 WebSocketWebSocket 是什么Spring Boot 如何简化 We

java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)

《java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)》:本文主要介绍java中pdf模版填充表单踩坑的相关资料,OpenPDF、iText、PDFBox是三... 目录准备Pdf模版方法1:itextpdf7填充表单(1)加入依赖(2)代码(3)遇到的问题方法2:pd