分析轮子- 十进制整数怎么变成无符号二进制的整数的

Posted godtrue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分析轮子- 十进制整数怎么变成无符号二进制的整数的相关的知识,希望对你有一定的参考价值。

前言:在 分析轮子(二)- << ,>>,>> (左移、右移、无符号右移)的时候发现十进制数转二进制数的时候,负数的位数是够的,比如:负整数 -15 的二进制表示是:11111111111111111111111111110001 ,但是 正整数 15 的二进制表示是:1111,抱着好奇心,我看了一下源码,现分析如下。

注:玩的是JDK1.7版

一:请先看一下如下资料,他们解释了计算机中为什么使用二进制表示数据?计算机中正数、零、负数是如何表示的以及为什么?

1):关于2的补码 (阮大神的佳作,通俗易懂)

2):[java]负数的二进制编码——越是基础的越是要掌握(这篇也很好,讲解的比较系统)

3):你真的了解Java中的负数

4):计算机中二进制数据的编码方式,整理了两篇他人的博客

 

二:整数的十转二(转八、转十六的底层也是一样),源码如下

   /**
     * Returns a string representation of the integer argument as an
     * unsigned integer in base&nbsp;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&nbsp;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>‘&#92;u0030‘</code>); otherwise, the first character of
     * the representation of the unsigned magnitude will not be the
     * zero character. The characters {@code ‘0‘}
     * (<code>‘&#92;u0030‘</code>) and {@code ‘1‘}
     * (<code>‘&#92;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&nbsp;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&nbsp;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("[ 二进制 与 操作]
")
                    .append("[").append("
")
                    .append("     ").append(toUnsignedStringNoLog(i,1)).append(" 十进制是:").append(i).append("
")
                    .append("   & ").append(toUnsignedStringNoLog(mask,1)).append(" 十进制是:").append(mask).append("
")
                    .append("   = ").append(toUnsignedStringNoLog((i & mask),1)).append(" 十进制是:").append((i & mask)).append("
")
                    .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(" ]


");
            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,这也是上述代码计算的一个思路,上面的代码比较简单,不妨自己动手玩玩吧!

 

以上是关于分析轮子- 十进制整数怎么变成无符号二进制的整数的的主要内容,如果未能解决你的问题,请参考以下文章

2021-10-22:颠倒二进制位。颠倒给定的 32 位无符号整数的二进制位。提示:请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不

asp 如何将中文转成无符号十进制整数(50分)

Java不提供无符号整数类型?谢谢

整数编码

语句“PHP不支持无符号整数”实际上意味着什么?

带无符号整数的 QSpinBox 用于十六进制输入