最清晰的进制转换讲解 - java实现

子曰:知之为不知,不知为不知,太菜也!

进制转换在平时的算法练习题或者项目中都会遇到,下面我们来看一下使用java如何进行进制互相转换。

1. 使用内置函数进行进制转换

在java函数中,Integer类中已经有相应函数可以进行十进制 与 二进制,八进制和十六进制的转换。

十进制转换其他进制
  //十进制转成二进制 
  Integer.toBinaryString(int i) 
  //十进制转成八进制 
  Integer.toOctalString(int i) 
  //十进制转成十六进制: 
  Integer.toHexString(int i)
其他进制转换十进制

   //二进制转十进制 
  Integer.valueOf("10111",2).toString()
  //八进制转成十进制 
  Integer.valueOf("188",8).toString() 
  //十六进制转成十进制 
  Integer.valueOf("ABC",16).toString() 

使用java提供的函数进行进制转换只能进行二进制,八进制,十进制和十六进制间的转换,这可能还不能满足我们的需求,那么如何实现任意进制的转换呢。

2. 模运算

可以使用离散数学的模运算来进行进制的转换。具体的计算过程这里就不赘述了,下面是相应代码。

十进制到任意进制的转换
   public static String Dec2Any(int n, int k) {
        // 存放取模的栈
        Stack s = new Stack<Integer>();

        // 用来储存结果串
        StringBuffer result = new StringBuffer("");

        // 对十进制数字进行取模运算,余数压栈,除的结果进入下次循环
        while (n != 0) {
            s.push(n % k);
            n = n / k;
        }

        // 将栈中的元素加入string中
        while (!s.isEmpty())
            result.append(s.pop());

        return result.toString();
    }

上面的代码需要注意一个问题,就是把十进制的数转为超过十进制的数字时,需要用相应的多进制表示符号替换。

任意进制到十进制数的转换

代码如下

    public static int Any2Dec(String num,int k){

        // 最终产生的10进制结果
        int res = 0;

        // 用来记录个位,十位,百位...
        int flag = 0;

        // 从末尾一直遍历整个字符串(也就是从个位开始到最高位)
        for (int i = num.length()-1;i >= 0;i--)
        {
            // 记录相对应的位置上的值
            int value = num.charAt(i);
            // 记录此位置上的单位大小,类比10进制就是 个位、十位、百位上的数字分别代表个十百
            int unit = (int)Math.pow(k, flag++);

            // 某一位置上的值 = 单价(个十百) * 数字
            if (num.charAt(i) >= 'A')
                // 如果大于A表示,超过了10,要还原成数字
                res += unit * (value -'A'+ 10);
            else
                res += unit * (value -'0');

        }
        return res;
    }
3. 位运算

以前进行进制转换,我也只会想到使用模运算,但是后来通过查看java的Integer中进制转换的源码,发现java内部是使用位运算进行进制转换的。

我们首先看十进制到其他进制的转换,相应函数的源码如下

// 这里的i就是进制对应的二进制表示位数,8就是3位,16进制就是4位
   public static String toBinaryString(int i) {
        return toUnsignedString(i, 1);
    }
   public static String toOctalString(int i) {
        return toUnsignedString(i, 3);
    }
        public static String toHexString(int i) {
        return toUnsignedString(i, 4);
    }

我们可以看到,内部是通过调用了toUnsignedString(int i)这个函数来实现进制转换的。

 	/**
     * 将整数转换为无符号数
     */
    private static String toUnsignedString0(int val, int shift) {
        // 确保转化位数的值在1-5之间,否则就会报错
        int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
        int chars = Math.max(((mag + (shift - 1)) / shift), 1);
        if (COMPACT_STRINGS) {
            byte[] buf = new byte[chars];
            formatUnsignedInt(val, shift, buf, 0, chars);
            return new String(buf, LATIN1);
        } else {
            byte[] buf = new byte[chars * 2];
            formatUnsignedIntUTF16(val, shift, buf, 0, chars);
            return new String(buf, UTF16);
        }
    }

这里对其中的一些值进行常亮解释

//用于表示二进制补码形式的值的位数
public static final int SIZE = 32;

numberOfLeadingZeros函数解释

 // 计算i当中第一个不为0的位置,最左边的一位,左边的值(0) < 右边的值(n)
 public static int numberOfLeadingZeros(int i) {
       //返回32,如果指定值的补码表示形式中没有1出现
       if (i <= 0)
           return i == 0 ? 32 : 0;
       int n = 31;
       // 类似于二分法的查找
       // 如果i大于第十六为1的值,那么n的值在前16位,故 n = n -16
       // 此时i最左侧的值不会出现在后16位, >>>=16 表示右移的数目为16
       // 依次地推,直到最后找见1 的最左侧位
       if (i >= 1 << 16) { n -= 16; i >>>= 16; }
       if (i >= 1 <<  8) { n -=  8; i >>>=  8; }
       if (i >= 1 <<  4) { n -=  4; i >>>=  4; }
       if (i >= 1 <<  2) { n -=  2; i >>>=  2; }
       return n - (i >>> 1);
   }

因为我对位运算不是很熟悉,因此看起来还是挺有难度的,这里对函数进行一个解析,以十进制转为十六进制toUnsignedString(i, 4)为例。

  • int radix = 1 << shift这一行,把1左移4位,得到radix为16
  • int mask = radix - 1得到mask为15,其二进制形式为00001111
  • buf[–charPos] = digits[i & mask]中,i & mask的目的就是取i的最后4位,然后在digits表单中取到相应的十六进制字符。
  • i >>>= shift,这一行,抛弃刚处理过的最后4位,往右再推4位,然后进入循环,直到i为0;
    所有的十六进制的字符都放在了buf里面,然后组合成一个字符串,返回。

我们可以通过改写这个函数,把十进制的数转换为任意2次方进制的数。

把其他进制转换为十进制时,内部是调用了Integer的parseInt(String s, int radix)方法。代码如下:

 public static int parseInt(String s, int radix)
                throws NumberFormatException
    {
        /*
         * WARNING: This method may be invoked early during VM initialization
         * before IntegerCache is initialized. Care must be taken to not use
         * the valueOf method.
         */

        if (s == null) {
            throw new NumberFormatException("null");
        }

        if (radix < Character.MIN_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " less than Character.MIN_RADIX");
        }

        if (radix > Character.MAX_RADIX) {
            throw new NumberFormatException("radix " + radix +
                                            " greater than Character.MAX_RADIX");
        }

        int result = 0;
        boolean negative = false;
        int i = 0, len = s.length();
        int limit = -Integer.MAX_VALUE;
        int multmin;
        int digit;

        if (len > 0) {
            char firstChar = s.charAt(0);
            if (firstChar < '0') { // Possible leading "+" or "-"
                if (firstChar == '-') {
                    negative = true;
                    limit = Integer.MIN_VALUE;
                } else if (firstChar != '+')
                    throw NumberFormatException.forInputString(s);

                if (len == 1) // Cannot have lone "+" or "-"
                    throw NumberFormatException.forInputString(s);
                i++;
            }
            multmin = limit / radix;
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                digit = Character.digit(s.charAt(i++),radix);
                if (digit < 0) {
                    throw NumberFormatException.forInputString(s);
                }
                if (result < multmin) {
                    throw NumberFormatException.forInputString(s);
                }
                result *= radix;
                if (result < limit + digit) {
                    throw NumberFormatException.forInputString(s);
                }
                result -= digit;
            }
        } else {
            throw NumberFormatException.forInputString(s);
        }
        return negative ? result : -result;
    }

核心代码是下面这部分

 while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                digit = Character.digit(s.charAt(i++),radix);
                if (digit < 0) {
                    throw NumberFormatException.forInputString(s);
                }
                if (result < multmin) {
                    throw NumberFormatException.forInputString(s);
                }
                result *= radix;
                if (result < limit + digit) {
                    throw NumberFormatException.forInputString(s);
                }
                result -= digit;
            }

通过学习Inreger进制转换方法的实现过程,读者可以自己对其进行改造从而实现自己需要完成的功能。毕竟使用位运算进行进制转换的效率会高一些。


版权声明:本文为CSDN博主「胡瓜_」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cwfjimogudan/article/details/53087303


你懂了没?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是程序的代码: ``` #include <iostream> #include <string> #include <cmath> using namespace std; // 将二进制字符串转为十进制数 int binToDec(string bin) { int dec = 0; for (int i = 0; i < bin.length(); i++) { dec += (bin[i] - '0') * pow(2, bin.length() - i - 1); } return dec; } // 将十进制数转为二进制字符串 string decToBin(int dec) { string bin = ""; while (dec != 0) { bin = to_string(dec % 2) + bin; dec /= 2; } return bin; } // 将二进制字符串转为八进制字符串 string binToOct(string bin) { string oct = ""; int len = bin.length(); // 补全到3的倍数 while (len % 3 != 0) { bin = "0" + bin; len = bin.length(); } // 每三位二进制转为一位八进制 for (int i = 0; i < len; i += 3) { string sub = bin.substr(i, 3); int dec = binToDec(sub); oct += to_string(dec); } return oct; } // 将八进制字符串转为二进制字符串 string octToBin(string oct) { string bin = ""; int len = oct.length(); // 每一位八进制转为3位二进制 for (int i = 0; i < len; i++) { int dec = oct[i] - '0'; string sub = decToBin(dec); // 不足3位补全 while (sub.length() < 3) { sub = "0" + sub; } bin += sub; } // 去掉前导0 int pos = bin.find_first_not_of('0'); if (pos != string::npos) { bin = bin.substr(pos); } else { bin = "0"; } return bin; } // 将二进制字符串转为十六进制字符串 string binToHex(string bin) { string hex = ""; int len = bin.length(); // 补全到4的倍数 while (len % 4 != 0) { bin = "0" + bin; len = bin.length(); } // 每四位二进制转为一位十六进制 for (int i = 0; i < len; i += 4) { string sub = bin.substr(i, 4); int dec = binToDec(sub); if (dec < 10) { hex += to_string(dec); } else { hex += (char)('A' + dec - 10); } } return hex; } // 将十六进制字符串转为二进制字符串 string hexToBin(string hex) { string bin = ""; int len = hex.length(); // 每一位十六进制转为4位二进制 for (int i = 0; i < len; i++) { int dec = 0; if (hex[i] >= '0' && hex[i] <= '9') { dec = hex[i] - '0'; } else if (hex[i] >= 'A' && hex[i] <= 'F') { dec = hex[i] - 'A' + 10; } string sub = decToBin(dec); // 不足4位补全 while (sub.length() < 4) { sub = "0" + sub; } bin += sub; } // 去掉前导0 int pos = bin.find_first_not_of('0'); if (pos != string::npos) { bin = bin.substr(pos); } else { bin = "0"; } return bin; } int main() { int choice1, choice2; string num; cout << "一级菜单:" << endl; cout << "(1)2进制数转换" << endl; cout << "(2)8进制数转换" << endl; cout << "(3)10进制数转换" << endl; cout << "(4)16进制数转换" << endl; cout << "(0)退出" << endl; cout << "请输入您的选择:"; cin >> choice1; while (choice1 != 0) { switch (choice1) { case 1: cout << "二级菜单:" << endl; cout << "(1)2进制数 ---> 8进制数" << endl; cout << "(2)2进制数 ---> 10进制数" << endl; cout << "(3)2进制数 ---> 16进制数" << endl; cout << "(0)返回上级目录" << endl; cout << "请输入您的选择:"; cin >> choice2; while (choice2 != 0) { cout << "请输入一个二进制数:"; cin >> num; switch (choice2) { case 1: cout << "转换结果为:" << binToOct(num) << endl; break; case 2: cout << "转换结果为:" << binToDec(num) << endl; break; case 3: cout << "转换结果为:" << binToHex(num) << endl; break; default: cout << "输入错误,请重新输入!" << endl; break; } cout << "请输入您的选择:"; cin >> choice2; } break; case 2: cout << "二级菜单:" << endl; cout << "(1)8进制数 ---> 2进制数" << endl; cout << "(2)8进制数 ---> 10进制数" << endl; cout << "(3)8进制数 ---> 16进制数" << endl; cout << "(0)返回上级目录" << endl; cout << "请输入您的选择:"; cin >> choice2; while (choice2 != 0) { cout << "请输入一个八进制数:"; cin >> num; switch (choice2) { case 1: cout << "转换结果为:" << octToBin(num) << endl; break; case 2: cout << "转换结果为:" << stoi(num, nullptr, 8) << endl; break; case 3: cout << "转换结果为:" << binToHex(octToBin(num)) << endl; break; default: cout << "输入错误,请重新输入!" << endl; break; } cout << "请输入您的选择:"; cin >> choice2; } break; case 3: cout << "二级菜单:" << endl; cout << "(1)10进制数 ---> 2进制数" << endl; cout << "(2)10进制数 ---> 8进制数" << endl; cout << "(3)10进制数 ---> 16进制数" << endl; cout << "(0)返回上级目录" << endl; cout << "请输入您的选择:"; cin >> choice2; while (choice2 != 0) { cout << "请输入一个十进制数:"; cin >> num; switch (choice2) { case 1: cout << "转换结果为:" << decToBin(stoi(num)) << endl; break; case 2: cout << "转换结果为:" << oct(stoi(num)) << endl; break; case 3: cout << "转换结果为:" << hex << stoi(num) << endl; break; default: cout << "输入错误,请重新输入!" << endl; break; } cout << "请输入您的选择:"; cin >> choice2; } break; case 4: cout << "二级菜单:" << endl; cout << "(1)16进制数 ---> 2进制数" << endl; cout << "(2)16进制数 ---> 8进制数" << endl; cout << "(3)16进制数 ---> 10进制数" << endl; cout << "(0)返回上级目录" << endl; cout << "请输入您的选择:"; cin >> choice2; while (choice2 != 0) { cout << "请输入一个十六进制数:"; cin >> num; switch (choice2) { case 1: cout << "转换结果为:" << hexToBin(num) << endl; break; case 2: cout << "转换结果为:" << binToOct(hexToBin(num)) << endl; break; case 3: cout << "转换结果为:" << stoi(num, nullptr, 16) << endl; break; default: cout << "输入错误,请重新输入!" << endl; break; } cout << "请输入您的选择:"; cin >> choice2; } break; default: cout << "输入错误,请重新输入!" << endl; break; } cout << "一级菜单:" << endl; cout << "(1)2进制数转换" << endl; cout << "(2)8进制数转换" << endl; cout << "(3)10进制数转换" << endl; cout << "(4)16进制数转换" << endl; cout << "(0)退出" << endl; cout << "请输入您的选择:"; cin >> choice1; } return 0; } ``` 这个程序可以实现二进制、八进制、十进制、十六进制之间的相互转换。在输入选择后,会进入对应的二级菜单,再根据提示输入转换的数值即可得到转换结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值