Java使用bit array实现二进制,十进制,十六进制值之间的转换

本文详细介绍了二进制、有符号无符号概念、正负数转化、十六进制以及十进制之间的转换。重点讲解了Java中如何使用bit array实现这些转换,包括补码在负数转换中的应用,并提供了BaseConverter类的源代码作为示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果读者还对bit数组还不太了解,可以去看一下我的另一篇文章,那里对bit数组和位运算都做了解释:

http://blog.youkuaiyun.com/zimu666/article/details/8284906

二进制

二进制的表达非常简单,只有0和1两个值,又因为他在数字电路中非常容易实现和处理,所以现代计算机几乎全部使用二进制来做为基础运算方式.

二进制运算方式是逢2进1.一组二进制数的值是通过这一个bit位的值,与相应的位指数想乘的结果.

在十进制中表达一个5位数,他们的指数排列是这样的:

指数43210
21160

这个十进制值的结果就是2 * 10^4 + 1 * 10^3 + 1 * 10^2 + 6 * 10^1 + 0 * 10^0 = 21160

如果在数的后面添加一个0,这个数就在十进制中进了一位,也就是以前数的十倍

二进制的表现方式相同,在讨论二进制的时候,通常需要说明一下哪一位是最高位(most significant bit),哪一位是最低位(least significant bit).

大部分使用的都是最低位在二进制数的最右边.

指数76543210
01101010

与十进制的运算原理一样: 1 * 2^6 + 1 * 2^5 +1 * 2^3 +1 * 2^1 = 64 + 8 + 2 = 74

同理,如果向这个数(指的是二进制数0110 1010)后面添加一个0,这个数也想当于进了一位,只不过进位后的数是以前数的两倍.



signed/unsigned

这里需要讨论一下signed(有符号)和unsgined(无符号)

什么是符号呢?

一个二进制数的最高位(一般情况下是最左边的一位)被称为符号位,这个位置上面的值(0或1)表示了这个二进制数是不是一个负数,例如

一个8-bit的二进制数,例子中会尽量使用byte格式的数,因为整数类型的太长了,不便于操作

10000001
这里的最高位是1,如果当作有符号数来处理,那么这个二进制数的结果就是 -1 * 2^7 + 1 * 2^0 = -128 + 1 = -127

通过这个计算我们也可以看出来,一个byte可以容纳的最小数-为128,二进制形式

1000 0000

一个byte可以容纳数的最大值为127

0111 1111

如果将byte的最大数 127 与 1 相加 会发生什么?

 

 01111111
+00000001
=10000000

这个时候有一个余数1进位到了第八位,byte值的最高位,而这个最高位会被当作负数处理,结果就变成了-128


这里可以顺便提一下著名的2038年现象.

在类unix操作系统中普遍使用一个32-bit的int值来表示时间,时间每加1秒,这个int数就会增加1.如果按照上面的例子来看,当除了最高位以外所有位都为1时,这个数就到了int可以容纳的最大值(Integer.MAX_VALUE,).时间如果再前进一秒(+1),这个数就变成了int的最小值

这个情况发生的时间就是在2038年1月19日的3点14分07秒.下一步可能会发生的情况有2说: 1是时间会回到1970年1月1日,2是时间会回到1901年12月13日,这两个结果是根据具体的解析方式得来的(要看unix具体是怎么处理负数).



如果最高位是无符号的呢?那127 + 1的结果 1000 000 就可以被当作正数来处理

在Java中,没有unsigned这种数据类型修饰,所以如果需要表示一个unsgined value,就必须要一个比他位数更大的其他数据类型来表示.

比如byte可以用short来表示,int可以用long来表示.


正负数之间的转化

一个比较熟悉二进制数的人可以很轻松松的做到将正十进制数转为二进制数,或者是将正负二进制数转成十进制,但是如果直接将一个负数转成二进制确没有那么容易,一般都会使用补码(2-complement).将一个二进制数进行取反操作,获得反码,然后增加1(补码),就可以获得这个数的相反值了.

比如我们想要获得-12在二进制的表示方法:

1200001100
取反11110011
补码 + 100000001
-1211110100



补码最大的优点体现在计算机内部运算的逻辑电路中.有了补码运算,在计算机内部进行加减法操作的时候不需要创建两个独立的运算逻辑分别对应加减法,只需要一个加法电路外加一个补码电路,就可以对所有的值进行加减运算.



十六进制

十六进制在在数学中来解释就是逢16进1,一般使用0-9的数字来代表0-9,A-F来代表10-15

比如十进制数32,在十六进制下的表示为20,十进制数13,在十六进制下为C

像java,c这样的语言为了区分十六进制和十进制数值,会在十六进制数的前面加上 0x,比如0x20是十进制的32,而不是十进制的20

十六进制数计算机领域的运用非常广泛,因为他可以非常简单的展现一个数的二进制写法

我们列出十六进制值和二进制值的表:

00000
10001
20010
30011
40100
50101
60110
70111
81000
91001
A1010
B1011
C1100
D1101
E1110
F1111

上面的图可以很清晰的看出,二进制需要4-bit的值来表现一个0-F的十六进制值

如果我们有一个32-bit的二进制值,想把他转换成十六进制,可以对照上面的表很轻松的将这个书写出来

01100100111111111001101110001010
64FF9B8A
这个十六进制的值就是 0x64FF98A.设想一下如果想要把这个数解析成十进制...会有多复杂

相反我们也可以很方便的将一个十六进制的值解析为二进制值.这里就不做示范了

十进制

十进制相比大家都知道了,每天都在用.使用十进制的原因,大概与人有十根手指有关,否则为什么会有这么多古代独立文明使用十进制呢.


介绍完上面的概念,就可以观看我们的程序了


BaseConverter类源代码

这里我还是刻意使用了以bit为单位的操作,为了是让大家看明白其中每一位是怎么解析的

package bitmap;

public class BaseConverter extends Bitmap {

	public static final int BINARY = 0;
	public static final int DECIMAL = 1;
	public static final int HEXADECIMAL = 2;
	public static final int FLOAT = 3;
	//public static final int OCTAL = 3; unimplemented
	
	private final String hexRegex = "0x[\\dABCDEFabcdef]{1,8}";
	private final String binRegex = "[01]{1,32}";
	private final String decRegex = "-?\\d{1,10}";
	//private finalt String octRegex = "[01234567]{1,11}"; unimplemented
	
	public BaseConverter(int value) {
		this(String.valueOf(value), DECIMAL);
	}
	
	/**
	 * Support only values in range of an Integer.
	 * Values larger than Integer.MAX_VALUE or less than Integer.MIN_Value
	 * will lead to unwanted result.
	 * @param value value in String format
	 * @param format supported are : BINARY, DECIMAL and HEXADECIMAL
	 */
	public BaseConverter(String value, int format) {
		super(32);
		
		try {
			if (format == BINARY)
				setBinary(value);
			else if (format == DECIMAL)
				setDecimal(value);
			else if (format == HEXADECIMAL)
				setHexadecimal(value);
			else 
				throw new IllegalArgumentException("Unsupportet numerical format");
		} catch (IllegalArgumentException e) {
			System.out.println("Unable to parse data:");
			System.out.println(e.getMessage());
		}
	}
		
	private void setBinary(String value) 
			throws IllegalArgumentException {
		if (!value.matches(binRegex))
			throw new IllegalArgumentException("Data is not in binary format");
	
		//since the value is inverted
		for (int i = value.length() - 1, j = 0; i >= 0; i--, j++)
			if (value.charAt(i) == '1')
				set(j);
	
	}
	
	private void setDecimal(String value) 
			throws IllegalArgumentException {
		if (!value.matches(decRegex))
			throw new IllegalArgumentException("Data is not in decimal format");
		
		int bitOffset = 0;
		int integer = Integer.parseInt(value);
		
		if (integer < 0) {//signed int
			set(31);
			integer += 1 << 31;//integer overflow will clear bit 31
		}
		while (integer > 0) {
			if ((integer & 1) == 1)
				set(bitOffset);
			integer = integer >> 1;
			bitOffset++;
		}
	}
	
	private void setHexadecimal(String value) 
			throws IllegalArgumentException {
		if (!value.matches(hexRegex))
			throw new IllegalArgumentException("Data is not in hexadecimal format");
		
		int BCDOffset = 0; //4bits-unit
		
		//0x are ignored
		for (int i = value.length() - 1; i > 1; i--) {
			byte b = hexToByte(value.charAt(i));
			for (int j = 0; j < 4; j++)
				if ((b & (1 << j)) >= 1)
					set(j + BCDOffset * 4);
			BCDOffset++;
		}
	}
	
	@Override
	public String toString() {
		return toBin();
	}
	
	/**
	 * String returned from super.toString is inverted with
	 * most significant bit on the rightmost position.
	 * We will have to make a reverse of it.
	 * @return binary representation of bitmap as String
	 */
	public String toBin() {
		StringBuilder result = new StringBuilder(super.toString());
		return result.reverse().toString();
	}
	
	/**
	 * Return decimal represenation of bitmap (signed int)
	 * The leftmost bit(bit 31) is used as sign.
	 * @return bitmap value in deciamal form(integer)
	 */
	public int toDec() {
		int result = 0;
		int mask = 1;
		
		for (int i = 0; i < size - 1; i++) {//skip bit 31
			if (get(i))
				result += mask;	
			mask = mask << 1;
		}
		if (get(31))// check bit 31(signed)
			result -= 1 << 31;
		
		return result;
	}
	
	public int toInt() {
		return toDec();
	}
	
	/**
	 * Return decimal representaion of bitmap (unsigned int)
	 * Type long is required because the leftmost bit will cause 
	 * a <Integer Overflow> with type int
	 * @return unsigned long value of the bitmap
	 */
	public long toUnsignedLong() {
		long result = 0;
		long mask = 1;
		
		for (int i = 0; i < size; i++) {
			if (get(i)) 
				result += (long) mask;
			mask = mask << 1;
		}
		
		return result;
	}
	
	/**
	 * Return hexadecimal representation of bitmap
	 * @return bitmap value in hexadecimal form
	 */
	public String toHex() {
		StringBuilder result = new StringBuilder("0x00000000"); //initialize stringbuilder
		byte value = 0;
		int bitOffset = 0;
		int resIndex = result.length() - 1;//index of result starting at rightmost position
		
		for (int i = 0; i < size; i++) {
			if (get(i))
				value += 1 << bitOffset;
				bitOffset++;
			if (bitOffset == 4) {
				result.setCharAt(resIndex--, getChar(value));
				value = 0;
				bitOffset = 0;
			}
		}
		
		return result.toString().replaceAll("0x[0]{1,7}", "0x");//clear all unnecessary zeros
	}
	
	private byte hexToByte(char chr) {
		chr = Character.toUpperCase(chr);
		if (chr < 57) //chr is less than 10 in ascii-code
			return (byte) (chr - 48);
		return (byte) (chr - 55); //'A' - 55 = 10;
	}
	
	private char getChar(byte value) {
		if (value < 10) 
			return (char) (value + 48); // 0 starts at 48
		return (char) (value + 55); // 'A'(10) starts at 65
	}

}


欢迎点评!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值