前言:在 分析轮子(二)- << ,>>,>> (左移、右移、无符号右移)的时候发现十进制数转二进制数的时候,负数的位数是够的,比如:负整数 -15 的二进制表示是:11111111111111111111111111110001 ,但是 正整数 15 的二进制表示是:1111,抱着好奇心,我看了一下源码,现分析如下。
注:玩的是JDK1.7版
一:请先看一下如下资料,他们解释了计算机中为什么使用二进制表示数据?计算机中正数、零、负数是如何表示的以及为什么?
1):关于2的补码 (阮大神的佳作,通俗易懂)
2):[java]负数的二进制编码——越是基础的越是要掌握(这篇也很好,讲解的比较系统)
3):你真的了解Java中的负数
二:整数的十转二(转八、转十六的底层也是一样),源码如下
/** * Returns a string representation of the integer argument as an * unsigned integer in base 2. * * <p>The unsigned integer value is the argument plus 2<sup>32</sup> * if the argument is negative; otherwise it is equal to the * argument. This value is converted to a string of ASCII digits * in binary (base 2) with no extra leading {@code 0}s. * If the unsigned magnitude is zero, it is represented by a * single zero character {@code ‘0‘} * (<code>‘\u0030‘</code>); otherwise, the first character of * the representation of the unsigned magnitude will not be the * zero character. The characters {@code ‘0‘} * (<code>‘\u0030‘</code>) and {@code ‘1‘} * (<code>‘\u0031‘</code>) are used as binary digits. * * @param i an integer to be converted to a string. * @return the string representation of the unsigned integer value * represented by the argument in binary (base 2). * @since JDK1.0.2 */ public static String toBinaryString(int i) { return toUnsignedString(i, 1); } /** * Convert the integer to an unsigned number. */ private static String toUnsignedString(int i, int shift) { char[] buf = new char[32]; int charPos = 32; int radix = 1 << shift; int mask = radix - 1; do { buf[--charPos] = digits[i & mask]; i >>>= shift; } while (i != 0); return new String(buf, charPos, (32 - charPos)); }
很明显,上述代码的核心是 toUnsignedString 方法的do-while循环,从低到高一位一位的确认转成的二进制数是0还是1,当待转换的整数经过无符号右移为0时,则停止循环。
三:改造后的源码,可通过日志信息更为直观的看到进制转换的每一步
/* * @description:进制转换测试类 * @author:godtrue * @create:2018-09-08 */ public class NumConvert { /** * *@description: 测试的入口方法 *@param args *@return: void *@author: godtrue *@createTime: 2018-09-08 *@version: v1.0 */ public static void main(String[] args) { System.out.println("整数 15 的二进制表示是:"+toBinaryString(15)); //System.out.println("整数 -1 的二进制表示是:"+toBinaryString(-1)); //System.out.println("整数 0 的二进制表示是:"+toBinaryString(0)); //System.out.println("整数 1 的二进制表示是:"+toBinaryString(1)); } /*** * *@description: Returns a string representation of the integer argument as an unsigned integer in base 2. *@param i 待转换十进制整数 *@return: java.lang.String *@author: godtrue *@createTime: 2018-09-08 *@version: v1.0 */ public static String toBinaryString(int i) { return toUnsignedStringPrintLog(i, 1); } /** * *@description: Convert the integer to an unsigned number,print log. *@param i 待转换十进制整数 *@param shift 移位的位数,转换进制时使用,用于获取转换进制的基数 *@return: java.lang.String *@author: godtrue *@createTime: 2018-09-08 *@version: v1.0 */ private static String toUnsignedStringPrintLog(int i, int shift) { char[] buf = new char[32]; for(int l=0; l<32; l++){ buf[l]=‘0‘; } int charPos = 32; int radix = 1 << shift; int mask = radix - 1; int loop = 1; do { buf[--charPos] = digits[i & mask]; StringBuilder logInfoMAndNum = new StringBuilder("[ 二进制 与 操作]\n") .append("[").append("\n") .append(" ").append(toUnsignedStringNoLog(i,1)).append(" 十进制是:").append(i).append("\n") .append(" & ").append(toUnsignedStringNoLog(mask,1)).append(" 十进制是:").append(mask).append("\n") .append(" = ").append(toUnsignedStringNoLog((i & mask),1)).append(" 十进制是:").append((i & mask)).append("\n") .append("]"); System.out.println(logInfoMAndNum); StringBuilder logInfoLoopCount = new StringBuilder("[ 第 ") .append(loop).append(" 次循环,转换的二进制数据表示为:").append(new String(buf)).append(" 计算出来的第 ").append(loop++).append(" 位,是:").append((i & mask)) .append("]"); System.out.println(logInfoLoopCount); StringBuilder logInfoConvertNum = new StringBuilder("[ 将") .append(" ] [ ").append(i) .append(" ] [ ").append("无符号右移 1 位,结果如右所示") .append(" ] [ ").append(toUnsignedStringNoLog(i,1)).append("(十进制是:").append(i).append(")").append(" >>> ").append(shift).append(" = ").append(toUnsignedStringNoLog((i >>> shift),1)).append("(十进制是:").append((i >>> shift)).append(")") .append(" ]\n\n\n"); System.out.println(logInfoConvertNum); i >>>= shift; } while (i != 0); return new String(buf); } /** * *@description: Convert the integer to an unsigned number,not print log. *@param i *@param shift *@return: java.lang.String *@author: godtrue *@createTime: 2018-09-08 *@version: v1.0 */ private static String toUnsignedStringNoLog(int i, int shift) { char[] buf = new char[32]; for(int l=0; l<32; l++){ buf[l]=‘0‘; } int charPos = 32; int radix = 1 << shift; int mask = radix - 1; do { buf[--charPos] = digits[i & mask]; i >>>= shift; } while (i != 0); return new String(buf); } /** * *@description: All possible chars for representing a number as a String *@param null *@return: *@author: godtrue *@createTime: 2018-09-08 *@version: v1.0 */ final static char[] digits = { ‘0‘ , ‘1‘ , ‘2‘ , ‘3‘ , ‘4‘ , ‘5‘ , ‘6‘ , ‘7‘ , ‘8‘ , ‘9‘ , ‘a‘ , ‘b‘ , ‘c‘ , ‘d‘ , ‘e‘ , ‘f‘ , ‘g‘ , ‘h‘ , ‘i‘ , ‘j‘ , ‘k‘ , ‘l‘ , ‘m‘ , ‘n‘ , ‘o‘ , ‘p‘ , ‘q‘ , ‘r‘ , ‘s‘ , ‘t‘ , ‘u‘ , ‘v‘ , ‘w‘ , ‘x‘ , ‘y‘ , ‘z‘ }; }
[ 二进制 与 操作] [ 00000000000000000000000000001111 十进制是:15 & 00000000000000000000000000000001 十进制是:1 = 00000000000000000000000000000001 十进制是:1 ] [ 第 1 次循环,转换的二进制数据表示为:00000000000000000000000000000001 计算出来的第 1 位,是:1] [ 将 ] [ 15 ] [ 无符号右移 1 位,结果如右所示 ] [ 00000000000000000000000000001111(十进制是:15) >>> 1 = 00000000000000000000000000000111(十进制是:7) ] [ 二进制 与 操作] [ 00000000000000000000000000000111 十进制是:7 & 00000000000000000000000000000001 十进制是:1 = 00000000000000000000000000000001 十进制是:1 ] [ 第 2 次循环,转换的二进制数据表示为:00000000000000000000000000000011 计算出来的第 2 位,是:1] [ 将 ] [ 7 ] [ 无符号右移 1 位,结果如右所示 ] [ 00000000000000000000000000000111(十进制是:7) >>> 1 = 00000000000000000000000000000011(十进制是:3) ] [ 二进制 与 操作] [ 00000000000000000000000000000011 十进制是:3 & 00000000000000000000000000000001 十进制是:1 = 00000000000000000000000000000001 十进制是:1 ] [ 第 3 次循环,转换的二进制数据表示为:00000000000000000000000000000111 计算出来的第 3 位,是:1] [ 将 ] [ 3 ] [ 无符号右移 1 位,结果如右所示 ] [ 00000000000000000000000000000011(十进制是:3) >>> 1 = 00000000000000000000000000000001(十进制是:1) ] [ 二进制 与 操作] [ 00000000000000000000000000000001 十进制是:1 & 00000000000000000000000000000001 十进制是:1 = 00000000000000000000000000000001 十进制是:1 ] [ 第 4 次循环,转换的二进制数据表示为:00000000000000000000000000001111 计算出来的第 4 位,是:1] [ 将 ] [ 1 ] [ 无符号右移 1 位,结果如右所示 ] [ 00000000000000000000000000000001(十进制是:1) >>> 1 = 00000000000000000000000000000000(十进制是:0) ] 整数 15 的二进制表示是:00000000000000000000000000001111 Process finished with exit code 0
仔细看上述代码及日志信息,可比较清楚的看到JDK是怎么将十进制的数据转换为对应的二进制数据的,核心还是通过 toUnsignedString 方法的 do-while 循环,从低到高一位一位的确认转成的二进制数是0还是1,当待转换的整数经过无符号右移为0时,则停止循环,因为再继续循环通过0和其他数据相与时总是为0的。可以调整上述测试代码类中的测试参数,查看相应的数据转换情况。
十进制数据 转换为 二进制数据 的逻辑是:将十进制的数据除以二,首先取余数作为二进制的第一位(从右到左,由低到高,左低右高),然后再用得到的商再除于二,再取余数作为二进制的第二位,以此类推,直到除不尽为止即商为0,比如:15转换为二进制数据
15/2=7 余数为1
7/2=3 余数为1
3/2=1 余数为1
1/2=0 余数为1
则对应的二进制数为 1111,如果是32位的整数,则将其余的位数补0 ,则15(十进制形式)= 0000 0000 0000 0000 0000 0000 0000 1111(二进制形式)
如果是 -15,转换为二进制,其步骤入下:
第一步:取负数的绝对值 |-15| = 15
第二步:将15转换为二进制,15(十进制形式)= 0000 0000 0000 0000 0000 0000 0000 1111(二进制形式)
第三步:取反 0000 0000 0000 0000 0000 0000 0000 1111(二进制形式)= 1111 1111 1111 1111 1111 1111 1111 0000(二进制形式)
第四步:加一 1111 1111 1111 1111 1111 1111 1111 0000 +1 = 1111 1111 1111 1111 1111 1111 1111 0001 ,所以,-15 = 1111 1111 1111 1111 1111 1111 1111 0001
负数 通过 无符号右移一位 之后,会变成比较大的一个正整数,这个正整数往往需要经过32次无符号右移一位,才能变成0,这也是上述代码计算的一个思路,上面的代码比较简单,不妨自己动手玩玩吧!
原文地址:https://www.cnblogs.com/godtrue/p/9596446.html