手写实现js进制转换

3,319 阅读4分钟

前言

最近看到越来越多的面试题都是手写算法或者手撕代码实现某些功能,我也饶有兴趣做起来,但发现有一个题,js实现进制转换网上的答案实在太少,而这一块部分同学的基础也比较薄弱,因此手写实现一个,帮你撸清进制间的转换

利用内置函数实现

说到进制转换,不得不首先说下js中的两个内置方法toStringparseInttoString可以把一个数转换为指定进制的数,parseInt是把数按照指定进制解析成十进制的数。

//toString用法 ,其中数前面加0默认为8进制,加0x默认为16进制。
var  = 10;
(10).toString(2);	//输出 "1010"
(10).toString(16);	//输出 "A"
(010).toString(10);	//输出 "8"
(0x10).toString(10);	//输出 "16"
//parseInt用法
parseInt("10", 2);	//返回 2
parseInt("10", 8);	//返回 8
parseInt("10", 10);	//返回 10
parseInt("AF", 16);	//返回 175

手写实现

既然要手写,那我们肯定得有思路才能用代码进行实现。为了讲解方便起见,我们用ES6中的 **取代Math.pow(radix,i)来表示数幂的关系。比如用8表示2的几次幂,可以用两种方法得到 Math.pow(2,3) === 2**3

我们知道js原生的toString方法,能转换的进制,进制最小为2,最大为36,因为阿拉伯数字加上英文字母也就是0-9,A-Z刚好加起来有36个,最多用这些字母来表示进制组成的数。进制map表为. var digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

(10).toString(2) // 返回 "1010" => 2**3*1 + 2**2*0 + 2**1*1 + 2**0*0
(1234).toString(10) // 返回 "1234" => 10**3*1 + 10**2*2 + 10*1*3 + 10**0*4
(110).toString(16) // 返回 "6e" => 16**1*6 + 16*0*14 .其中14来源于digits[14]
重要:上面=>前面的数,用进制思路也就是可以用=>后面的表达式来表示,需要理解推算。

比如把10转换为2进制,首先我们可以算10最接近2的几次幂,应该是8,用幂表示Math.pow(radix,i),其中radix是2,i是3,8的二进制是1000,8比10还小2,2用2进制表示为10,因此加起来就是1010

因此思路把所有数用十进制算术的可以表示为 radix**i*rate + radix**(i-1)*rate2 + radix**(i-2)*rate3...;
//"1234" => 10**3*1 + 10**2*2 + 10*1*3 + 10**0*4
为什么能得到上面的转换表达式呢,因为幂总是大于相加 2**3 >= 2**2+2.所以总是高位递减。理解这一步很重要。
function to_string(num,radix){ //把一个十进制数转换为传参的进制数
    if(num == 0) return num;
    if(!Number.isInteger(num)){//目前不支持小鼠
        console.warn('目前不支持小数转换');
        return ;
    }
    var res = '',rate,i;
    if(num < 0){//对负数的处理
        res += '-'
        num = Math.abs(num)
    }
    //下面循环为了获得最高的幂数,比如上面的110转换为16进制为6e,其中i为1;也就是16**1*6中的1;
    //获取最高的幂数是为了下面根据幂数来推算每一位的倍数和相应的字符
    for(i = 0;i<num;i++){
        if(Math.pow(radix,i) <= num && Math.pow(radix,i+1) > num){
            break;
        }
    }
    //开始根据最高幂数,也就是有幂数+1个字符,开始获取结果字符
    while(i>=0){
        rate = Math.floor(num/Math.pow(radix,i));//获得最高的幂数的倍数
        res += digits[rate]; //比如110转16进制为6e时,第一次rate为6,digits[6] === 6,第二次 rate为14,digits[14] === e;因此res经过2次相加为6e;
        num -= rate * Math.pow(radix,i); //由于算过最高位,因此减去已经算过的高位数.继续下一次计算
        i--;
    }
    return res; 
}

//把一个数按照传参的进制解析为十进制。
function parse_int(str,radix){ 
    str = (str+'').toUpperCase();
    var res = 0,abs = 1,len = str.length,i;
    if(str[0] === '-'){//对负数的处理
        abs = -1;
        str=str.substring(1)
    }
    for(i = len - 1;i >= 0; i--){
        res += digits.indexOf(str[len-1-i]) * Math.pow(radix,i)
    }
    return abs*res;
}

function convert_radix(str,source_radix,to_radix){ //把数按source_radix进制解析转换为to_radix进制
    return to_string(parse_int(str,source_radix),to_radix)
}

通过convert_radix方法,可以把一个数按照源进制转换为目标进制数。 比如convert_radix('1011', 2, 10) 得到11。

把十进制转换为其他进制调用to_string方法,比如to_string(10,2) 得到 1010.

把其他进制转换为十进制调用parse_int方法,比如parse_int('6e',16)得到110.

进制实现是自己写的,没有参考,因此有性能优化或者更好的方案,多多指教.