JS 怎么使用十六进制保存100位状态的问题

现在开发遇到一个需求,需要保存100个状态,状态只有01两种状态,如果使用字符串保存100个01,那么就会很长, 然后就想到了转成16进制

思考

但是在JS最大的安全整数 Number.MAX_SAFE_INTEGER 内最大也只能9007199254740991这么大。

// 将最大安全整数转成16进制
(Number.MAX_SAFE_INTEGER).toString(16)
// 0x1FFFFFFFFFFFFF

结果发现只有13个F,一个F可以表示4位(1111),即13*4=52正常只能保存52

// 转成二进制
parseInt('0xFFFFFFFFFFFFF').toString(2)
// 1111111111111111111111111111111111111111111111111111

// 计算下长度
parseInt('0xFFFFFFFFFFFFF').toString(2).length
// 52

如果要是再加一个F呢?14F(发现貌似没什么问题?!真的没问题吗??)

parseInt('0xFFFFFFFFFFFFFF')	// 72057594037927940

现在做下转换成二进制和计算下长度

// 转成二进制
parseInt('0xFFFFFFFFFFFFFF').toString(2)
// '100000000000000000000000000000000000000000000000000000000'

// 计算下长度
parseInt('0xFFFFFFFFFFFFFF').toString(2).length
// 57

可以看出已经出现问题了,转换成的二进制不对,长度也不对。
那么没办法了,在安全的范围内只有52位可以用(写代码也是要注意安全的,不然出错了,都不知为什么)

然后就想到了,使用两个不就有 52 * 2 = 104个了,那么就满足需要了

实现

保存数据的格式,我使用的是 . 进行数据分割,即 整数.小数 的数据格式。
那么就分开处理就可以了,我是优先使用整数部分,即 [0-49].[50-99]这种方式保存
后来发现一个问题不能直接使用位运算,因为位运算只支持32位以内的,这样就麻烦了
我是通过将十六进制转成二进制字符串,然后再转成十六进制,这样就不牵扯位运算了

关于位置的保存,由于数组是左边是0位,我们直接改变0位,那么就会造成初始的数据就会很大,所以我们要反过来,从数组的最高位开始改变。

// 第一位为1
1 '1000' => 8   '0001' => 1
// 第二位为1
3 '1100' => 8   '0011' => 3
// 第三位为1
7 '1110' => 8   '0111' => 7

我们使用后面的保存方式,这样数据转换之后就会从小到大了,也符合我们的习惯。

基础版转换

现在这个只能支持50位以内的

/**
 * 二进制增量运算
 * @param {String} data 十六进制
 * @param {Number} pos 位
 * @return {String} 计算后的十六进制值
 */
function addBinaryVal(data, pos) {
	// 超过50位的不进行转换了
	if (pos >= 50){
        return data;
    }
	// 先将十六进制转成二进制,再在前面填充 0,然后进行分割成数组,再替换指定位置的状态,最后再转成十六进制
    let binaryData = parseInt(data, 16).toString(2).padStart(50, '0').split('');
    // 改变指定位置
    binaryData[49-pos] = '1';

    return '0x' + parseInt(binaryData.join(''), 2).toString(16)
}

加强版转换

可以支持100位或者150位甚至更高,我这里只是100位,可以按照需要自行扩展
封装一个方法进行状态变更


/**
 * 将十六进制字符串指定位置值置为1
 * 对两个十六进制字符串单独计算,然后再还原
 * @param {String} data 数据 两个十六进制字符串
 * @param {Number} pos 位置 范围限制[0,99]
 * @return {String} 改变后的字符串
 */
function replaceBinaryValByPos(data, pos){
    if (pos > 100 || pos < 0){
        logger.error("getBinaryValByPos data: %j pos: %j is invalid");
        return data;
    }
    let tempData = data.split('.');
    if(pos < 50){
        tempData[0] = addBinaryVal(tempData[0], pos);
    }else{
        tempData[1] = addBinaryVal(tempData[1], pos-50);
    }
    return tempData.join('.');
}


let val = '0xa.0xa' // '1010.1010'
val = replaceBinaryValByPos(val, 0) // '1011.1010'
console.log(val) // '0xb.0xa'

val = replaceBinaryValByPos(val, 50) // '1010.1011'
console.log(val) // '0xa.0xb'

获取对应位的状态

封装一个方法固定方法获取数据的状态

/**
 * 十六进制字符串指定位置的状态
 * 最大安全整数转换成二进制 可以有53位 只取用50位,两个字符串就可以有100位
 * @param {String} data 数据 两个十六进制字符串
 * @param {Number}pos 位置 范围限制[0,99]
 * @return {boolean} 状态 [true,false]
 */
function getBinaryValByPos(data, pos){
    if (pos > 100 || pos < 0){
        logger.error("getBinaryValByPos data: %j pos: %j is invalid");
        // 无效状态返回true 表示已经生效,防止因未生效的重复操作
        return true;
    }

    if(pos < 50){
        let val = parseInt(data.split('.')[0], 16).toString(2).padStart(50, '0');
        return val.charAt((50-1) - pos) === '1';
    }else{
        let val = parseInt(data.split('.')[1] || '0x0', 16).toString(2).padStart(50, '0');
        return val.charAt((100-1) - pos) === '1';
    }
}

let val = '0xa.0xb' // ==> '1010.1011'
console.log(getBinaryValByPos(val,0))   // false
console.log(getBinaryValByPos(val,1))   // true
console.log(getBinaryValByPos(val,2))   // false

将十六进制的数据转换成二进制字符串

这里是将十六进制的数据转换成二进制字符串,不过数据状态是从左到右的保存
这样方便使用

/**
 * 获取二进制格式字符串
 * 反向组合 从左往右算
 * @param data 数据 两个十六进制字符串
 * @return {string} 二进制字符串
 */
function getBinaryStr(data) {
    let tempData = data.split('.');
    let len = 50;
    let strArr = [];
    strArr[0] = parseInt(tempData[0], 16).toString(2).padStart(len, '0').split('').reverse().join('');
    strArr[1] = parseInt(tempData[1], 16).toString(2).padStart(len, '0').split('').reverse().join('');
    return strArr.join('');
}


let val = '0xa.0xb'  // 1010  1011  翻转=> 01011101
val = getBinaryStr(val);
console.log(val)  // '0101000...0001101000...000' =>总长度100

结束

这里只是我想到的方法,如果有更好的方法,可以一起探讨交流
要是有更好的发现再更新,加油!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值