Java中整数基础知识

news2024/10/6 4:11:03

原文链接 Java中整数基础知识

最近做了一道题,非常有意思,题本身很简单,但涉及到整数的最大值以及最小值,当写测试用例的时候,却犯了一个错误,发现最小整数并不是0xFFFFFFFF,我们来仔细看一下。

整数基础

Java中,整数都是有符号的,最高位是符号位,0表示正数,1表示负数。有四种,byte,short,int和long。

  • byte 8位,-2^7 ~ 2^7 - 1,-128 ~ 127, 0x80 ~ 0x7F
  • short 16位,-2^15 ~ 2^15 - 1,-32768 ~ 32767, 0x8000 ~ 0x7FFF
  • int 32位,-2^31 ~ 2^31 -1,-2147483648 ~ 2147483647, 0x8000000 ~ 0x7FFFFFFF
System.out.println(String.format("Max byte %d, 0x%X, half 0x%X", Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE/2));
System.out.println(String.format("Min byte %d, 0x%X, half 0x%X", Byte.MIN_VALUE, Byte.MIN_VALUE, (byte)(Byte.MIN_VALUE/2)));
System.out.println(String.format("Max short %d, 0x%X, half 0x%X", Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE/2));
System.out.println(String.format("Min short %d, 0x%X, half 0x%X", Short.MIN_VALUE, Short.MIN_VALUE, (short) (Short.MIN_VALUE/2)));
System.out.println(String.format("Max Int %d, 0x%X, half 0x%X", Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE/2));
System.out.println(String.format("Min Int %d, 0x%X, half 0x%X", Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE/2));
        
// Outputs
//Max byte 127, 0x7F, half 0x3F
//Min byte -128, 0x80, half 0xC0
//Max short 32767, 0x7FFF, half 0x3FFF
//Min short -32768, 0x8000, half 0xC000
//Max Int 2147483647, 0x7FFFFFFF, half 0x3FFFFFFF
//Min Int -2147483648, 0x80000000, half 0xC0000000

细节和原理

值得注意的是,16进制的数值与直觉预期并不一样,特别是负数。正数是一致的,比如int,一共是32位,最高位是符号,所以真正数值部分是31位,那么最大的int就是0x7FFFFFF。

但负数,也即是最小的int,却与直觉完全不一样。按照直觉,负数最高位是1,那最小的int应该是0xFFFFFFFF啊,为何确是0x80000000呢?原因就是整数的编码方式并不是直接的二进制形式的,是以补码的形式,也就是说在内部实现中,用二进制表示一个整数的时候,是以二进制补码形式(转成二进制后还要求其补码,才是真实的二进制和16进制形式)。

简单来说,补码是一种二进制编码形式,正数的补码就是它的本身,而负数的补码是其取反后加1,可以参考百科上的定义。

负数的转换:原码取反加1,即是补码,补码再转补码即得到原码(也可以补码减1再取反即是原码),符号位在转换过程中一直不变。

**注意:**补码的严谨说法是2的补码(Twi’s Complement),并不称作二进制补码。补码是为了能用加法的方式来计算减法。因此原码转补码时,取反加1,补码求原码时再求补码,避免用减法(虽然减1后再取反也能求得原码,但需要用到减法)。

非10进制字面常量是补码形式

在日常代码中,为了方便,通常都用16进制来写一些整数常量,这里就特别要注意了。16进制的字面常量,不会再进行补码转换,会当成补码直接使用。

System.out.println(String.format("Literal %d, 0x%X", 0xffffffff, 0xffffffff));
//Literal -1, 0xFFFFFFFF

所以,你写的0xFFFFFFFF是补码形式,它的原码是减1再取反,(32个1)减1,最低位变成0,前面31个1,再取反,就只剩下最后一位是1和最高位的符号位,因此是-1,注意符号位是不变的,在转换过程中。

而最小的整数是-2^31,原码 形式应该是0x80000000,先取反变成了0xFFFFFFFF,再加1,符号位最高位不变的情况下,其余全变成了0,所以是0x80000000。

最小整数

开篇时说了,当时错误的认为0xFFFFFFFF是最小的整数,这里犯的第一个严重错误是,误把二进制的补码当成了原码,代码中的16进制(二进制)都是补码形式的,它的原码是0x80000001即-1。这个错误是比较明显的。

但另外的问题就是,假如都是二进制原码的情况下,为啥最小的整数是0x80000000而不是0xFFFFFFFFF。这是理解上的误区,整数的定义是,最高位是符号位,所以常规认知是全是1的情况是最大的数,加上符号不就变成最小的了么?这是以10进制思维,也就是二进制转换成为10进制后的想法。计算机只认识二进制,在最高位是1(负数)的情况下,哪个数最小?当然0x80000000最小啊,它除了符号位全是0,肯定 小于0xFFFFFFFF,因此从二进制的角度来理解,0x80000000是最小的整数。

而0xFFFFFFFF(原码)则是第2小的负整数,最高位是符号位,其余31位全是1,它的补码是0x80000001:

System.out.println(String.format("Literal %d 0x%X", 0x80000001, 0x80000001));
//Literal -2147483647 0x80000001

计算机中是以二进制补码来存储整数的,所以要从计算机的角度来理解比较,就是要用二进制的补码来比较两个数的大小。

再次强调,我们写的源码当中的二进制(无论是字面常量,还是打印输出)都是补码形式,计算机看到的也是补码,比较也是补码,只有当转换成为10进制时,才会还原为原码并进行10进制转换

由此得出,(注意,程序员眼睛看到的16进制全是补码形式):

  • 0x80000000是最小的负数,原码为0x80000000,-2^31
  • 0x80000001,第2小的负数(最小的0x80000000再加上1),原码为0xFFFFFFFF,-(2^31-1)
  • 0xFFFFFFFF,是-1,原码为0x80000001。它是最大的负数(-1是最大的负数)。0xFFFFFFFF(全是1)肯定 最大啊,最高位是1,是负数,所以是最大的负数。

一些有意思的值

Integer.MAX_VALUE + 1 = Integer.MIN_VALUE

按理说应该溢出了,但如果以16进制去计算,就是这样的结果:0x7FFFFFFF + 1 = 0x80000000

System.out.println(String.format("Max in %d (0x%X) + 1 = %d (0x%X)", Integer.MAX_VALUE, Integer.MAX_VALUE, (Integer.MAX_VALUE+1), (Integer.MAX_VALUE+1)));
// Max in 2147483647 (0x7FFFFFFF) + 1 = -2147483648 (0x80000000)

Integer.MIN_VALUE - 1 = Integer.MAX_VALUE

System.out.println(String.format("Min in %d (0x%X) - 1 = %d (0x%X)", Integer.MIN_VALUE, Integer.MIN_VALUE, (Integer.MIN_VALUE-1), (Integer.MIN_VALUE-1)));
//Min in -2147483648 (0x80000000) - 1 = 2147483647 (0x7FFFFFFF)

参考资料

  • 为什么0xffffffff是-1?(计算机对整型的存储)
  • 原码, 反码, 补码 详解
  • 一文读懂原码、反码与补码
  • 二进制的原码、反码、补码
  • 关于2的补码

原创不易,打赏点赞在看收藏分享 总要有一个吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1125301.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络协议--UDP:用户数据报协议

11.1 引言 UDP是一个简单的面向数据报的运输层协议:进程的每个输出操作都正好产生一个UDP数据报,并组装成一份待发送的IP数据报。这与面向流字符的协议不同,如TCP,应用程序产生的全体数据与真正发送的单个IP数据报可能没有什么联…

Citrix XenDesktop云桌面单点登录XenApp虚拟应用小技巧

哈喽大家好,欢迎来到虚拟化时代君(XNHCYL)。 “ 大家好,我是虚拟化时代君,一位潜心于互联网的技术宅男。这里每天为你分享各种你感兴趣的技术、教程、软件、资源、福利……(每天更新不间断,福利不见不散)” 第1章 前言 实现XenDesktop的桌面打开XenApp发布的应用…

为什么我们从github clone下来的maven项目本地运行报错

github上的项目clone到本地,比如是个Springboot的项目,我们用idea运行莫名其妙的报各种问题,常见的有以下异常: java.lang.NoClassDefFoundError:xxxxjava.lang.ClassNotFoundException:xxxxxjava.lang.NoSuchMethodE…

64 最长公共子序列

最长公共子序列 题解1 DP 给定两个字符串 text1 和 text2,返回这两个字符串的 最长公共子序列的长度。如果不存在 公共子序列,返回 0 。 一个字符串的子序列是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些…

【CCF】Z字形扫描

这题的关键是将整个扫描的过程,拆分成很多次斜着操作数组的过程。 而且这个过程中可以建立如下规律: (1)一斜线上的元素个数与切换到下一条斜线这一操作之间建立规律。 先讨论左上部分的数组: 1)当元素个…

本地部署Stackedit Markdown编辑器并通过cpolar内网穿透实现远程访问

文章目录 1. docker部署Stackedit2. 本地访问3. Linux 安装cpolar4. 配置Stackedit公网访问地址5. 公网远程访问Stackedit6. 固定Stackedit公网地址 StackEdit是一个受欢迎的Markdown编辑器,在GitHub上拥有20.7k Star!,它支持将Markdown笔记保…

指针-Pointer

0.1 地址 (1)字节(Byte) 一个字节存储8位无符号数(1Byte 8 bit),每个字节储存的数值范围为0-255 (2)内存 1G 1024M 、1M 1024K 、 1K 1024Byte 、1Byte 8 bit (3&#xff…

谈谈你对spring boot 3.0的理解

谈谈你对spring boot 3.0的理解 一,Spring Boot 3.0 的兼容性 Spring Boot 3.0 在兼容性方面做出了很大的努力,以支持存量项目和老项目。尽管如此,仍需注意以下几点: Java 版本要求:Spring Boot 3.0 要求使用 Java 1…

高等数学啃书汇总重难点(五)定积分

最近都在忙着刷题,尤其是政治和英语也开始加量复习了,该系列断更了将近2个月~不过最近在刷题的时候又遇到一些瓶颈,因此回归基础来整理一下知史点~ 总的来说,虽然第五章也是重中之重,定理数量也很多,但&…

通过条件竞争实现内核提权

条件竞争漏洞(Race Condition Vulnerability)是一种在多线程或多进程并发执行时可能导致不正确行为或数据损坏的安全问题。这种漏洞通常发生在多个线程或进程试图访问和修改共享资源(如内存、文件、网络连接等)时,由于…

There are not enough slots available in the system to satisfy the 48 slots报错

文章目录 问题描述解决办法 问题描述 多核运行时出现这个错误,减少核数运行正常 解决办法 输出命令 vim ~/.bashrc添加 alias mpirunmpirun --oversubscribe执行命令 source ~/.bashrc解决。

imu预积分学习(更新中)

imu预积分学习(更新中) IMU预积分可以做什么? 以上面那个经典图片为例子,IMU可以通过六轴数据,拿到第i帧和第j帧之间的相对位姿,这样不就可以去用来添加约束了吗 但是有一个比较大的问题是: I…

电脑技巧:27个Office使用小技巧,值得收藏

目录 一、Word 二、EXCEL 三、附文:Word和Excel快捷键 我们中的绝大部分人都使用微软的Office,但是我们是否都了解如何能够最有效地使用它?我们在这里列举了一些关于使用Word和Excel的窍门。 我们使用最多的软件可能就是办公软件了——字…

C++数据结构X篇_19_排序基本概念及冒泡排序(重点是核心代码)

文章目录 1. 排序基本概念2. 冒泡排序2.1 核心代码2.2 冒泡排序代码2.3 查看冒泡排序的时间消耗2.4 冒泡排序改进版减小时间消耗 1. 排序基本概念 现实生活中排序很重要,例如:淘宝按条件搜索的结果展示等。 概念 排序是计算机内经常进行的一种操作,其目…

计算机网络(谢希仁)第八版课后题答案(第一章)

1.计算机网络可以向用户提供哪些服务 连通性:计算机网络使上网用户之间可以交换信息,好像这些用户的计算机都可以彼此直接连通一样。 共享:指资源共享。可以是信息、软件,也可以是硬件共享。 2.试简述分组交换的要点 采用了存储转发技术。把报文(要发…

Python 测试框架unittest和pytest的优劣

一、Unittest Unittest是Python标准库中自带的单元测试框架,Unittest有时候也被称为PyUnit,就像JUnit是Java语言的标准单元测试框架一样,Unittest则是Python语言的标准单元测试框架。 Unittest支持自动化测试,测试用例的初始化、…

【Chrome】使用k8s、docker部署无头浏览器Headless,Java调用示例

什么是无头浏览器? 无头浏览器是一种没有图形用户界面的浏览器。无头浏览器不通过其图形用户界面(GUI)控制浏览器的操作,而是使用命令行。 为什么要用Chrome无头? Chrome Headless用于抓取(谷歌)、测试(开发者)和黑客(黑客)。搜索引擎&…

【单元测试】--高级主题

一、模拟与存根深入 在单元测试中,模拟(Mock)和存根(Stub)是两种常用的测试替代品,用于模拟外部依赖或模拟特定行为,以便测试能够独立运行。以下是深入了解模拟与存根的概念,以NUni…

opencv dnn模块 示例(19) 目标检测 object_detection 之 yolox

文章目录 0、前言1、网络介绍1.1、输入1.2、Backbone主干网络1.3、Neck1.4、Prediction预测输出1.4.1、Decoupled Head解耦头1.4.2、Anchor-Free1.4.3、标签分配1.4.4、Loss计算 1.5、Yolox-s、l、m、x系列1.6、轻量级网络研究1.6.1、轻量级网络1.6.2、数据增强的优缺点 1.7、Y…

多态的使用以及多态底层的实现(下)

经过之前的学习我们知道了,继承能够实现多态的原理就是,在继承的父类和子类中各自存在一个虚表,父类和子类的虚表中各自储存了自己的虚函数,不同的点就是如果我们完成了虚函数的重写,那么子类(派生类&#…