欢迎关注今日头条号、微信公众号、知乎号:仰望夜空一万次
随意聊聊并记录从小城市到上海工作生活的所思所想。
不去记录,有些事情都好像没有发生过。
本文介绍Java中bit的操作,涉及到原码、反码、补码的概念。最后引入编程珠玑中的题目实战如何使用bit的方式存储数据。
首先复习下原码、反码、补码的概念,为后续负整数的内存显示格式理解打好基础。
原码、反码、补码
在java虚拟机中整数有byte、short、int、long四种整数,分别表示 8位、16位、32位、64位有符号整数,使用补码表示。
- 原码
所谓原码就是符号位加上数字的二进制表示,int为例,第一位表示符号 (0正数 1负数)简单期间一个字节表示
+7的原码为: 00000111
-7的原码为: 10000111
对于原码来说,绝对值相等的正数和负数只有符号位不同。
- 反码
一个数如果为正,则它的反码与原码相同;一个数如果为负,则符号位为1,(符号位不变化,其余位数取反)。
换言之 该数的绝对值取反(绝对值取反各位都取反)。
为了简单起见,我们用1个字节来表示一个整数:
+7的反码为:00000111
-7的反码为: 11111000
- 补码
补码:一个数如果为正,则它的原码、反码、补码相同;一个数如果为负,补码为反码加1。
为了简单起见,我们用1个字节来表示一个整数:
+7的补码为: 00000111
-7的补码为: 11111001
正数:它的原码、反码、补码相同。
负数:反码符号位不变化,其余位数取反。补码符号位不变化其余各位原码取反加1。
对于byte类型的整数,对于负数的表示快速记忆方式
10000000 = -128 (-128 + 0)
10000001 = -127 (-128 + 1)
10000011 = -125 (-128 + 3)
10000111 = -121 (-128 + 7)
运算符
- 有符号右移运算符">>"
该数对应的二进制码整体右移,左边的用原有标志位补充,右边超出的部分舍弃。
代码示例
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class BitUnitTest {
@Test
public void byteTypeTest() {
byte i = 127; //byte 最大值
byte j = -128;//byte 最小值
//获得符号位bit的信息
int signI = (i >> 7) & 1;
int signJ = (j >> 7) & 1;
StringBuffer sb1=new StringBuffer();
//获得byte中每一位的bit信息
for(int k=0;k<=7;k++){
int m = (i >> k) & 1;
sb1.append(m);
}
System.out.println(sb1.reverse());// 01111111
StringBuffer sb2=new StringBuffer();
for(int k=0;k<=7;k++){
int n = (j >> k) & 1;
sb2.append(n);
}
System.out.println(sb2.reverse());// 10000000
//127 补码表示0111 1111,它的原码、反码、补码相同.当使用有符号右移运算符">>",右移7位后,得到127的符号位0
assertEquals(0,signI);
//-128 补码表示1000 0000,它的原码1000 0000、反码1111 1111、补码1000 0000.
// 当使用有符号右移运算符">>",右移7位后,得到-128的符号位1
assertEquals(1,signJ);
}
}
编程珠玑第一章问题解决
实现纲要
Java代码实现方式
import java.util.Random;
/**
* @author wang.baozhi
* @since 2019/10/21 下午8:27
*/
/*
* 使用bit方式存储大量int类型的数字
*/
public class BitTest{
public static void main(String[] args){
int totalCount = 100; //待排序数的数量
//java中int占4字节,一个字节包含8个bit,每一个bit可以代表一个数.计算初始化多少个int
int len = (totalCount - 1)/32 +1;
int[] storageIntArray = new int[len]; //声明并初始化
//初始化被存取的int数组
int[] inputIntArray = new int[totalCount];
//产生随机输入数据,数据是乱序的,可能重复
Random rand = new Random();
for(int i=0;i<totalCount;i++){
int randomInt=rand.nextInt(totalCount);
inputIntArray[i] = randomInt;
}
//将数据以bit方式存入int数组
for(int i=0;i<totalCount;i++){
set(storageIntArray,inputIntArray[i]);
}
//从小到大,输出存储的数字
for(int i=0;i<totalCount;i++){
if(read(storageIntArray,i)==1)
System.out.print(i+" ");
}
}
//将第index位置的int值进行bit操作
public static void set(int[] num,int i){
int index=i/32;//确定使用int数组中的下标
int position=i%32;//确定一个int表示32位bit中的哪一位bit
num[index] |= (1<<position);//将int值的position位置bit值设置为1
}
//读取第i位,判断是0/1
public static int read(int[] num,int i){
int index=i/32;//确定使用int数组中的下标
int position=i%32;//确定一个int表示32位bit中的哪一位bit
int result=(num[index] >> position) & 1;//判断第index个int值得第position个bit值是否是1
return result;
}
}
输出结果(每次不同):
3 4 5 6 7 8 9 10 14 15 16 19 21 23 25 26 28 29 32 33 34 35 36 37 38 40 41 42 44 45 46 49 53 54 55 56 58 59 60 61 62 63 65 66 67 68 71 73 74 75 76 78 79 80 81 83 89 91 92 94 95 96 97 98
如果将不重复的从0到99的int类型数字,放入4个int组成的int数组中,int数组存储情况?
答:
31-0存入第一个int,63-32存入第二个int,95-64存入第三个int,127-96存入第四个int。
前3个int值得每一位bit都是1,因为int是以补码表示,32位bit都是1的int表示为-1。
96-99这四个数字存储在第四个int值的从右到左的四位bit,int值为显示为15。
所以四个int值得表现为int[-1,-1,-1,15]。
打印int值为-1,在内存中每一位bit的存取情况
@Test
public void intTypeTest() {
int i = -1;
StringBuffer sb=new StringBuffer();
for(int k=0;k<=31;k++){
int n = (i >> k) & 1;
sb.append(n);
}
System.out.println(sb.reverse());
//int类型-1,每一位bit打印的结果为11111111111111111111111111111111
}
参考
- Primitive Data Types
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
- Two’s complement
https://stackoverflow.com/questions/3621067/why-is-the-range-of-bytes-128-to-127-in-java