考虑一个十六进制整数值,如n = 0x12345
,如何通过执行remove(n, 3)
(big-endian(来获得0x1235
作为结果?
对于上面的输入,我认为这可以通过执行一些比特步骤来实现:
partA
=从索引0
到targetIndex - 1
提取部分(应返回0x123
(partB
=从targetIndex + 1
到length(value) - 1
提取部分(0x5
(- 结果,可以用
((partA << length(partB) | partB)
表示,得到0x1235
的结果
然而,一旦每个十六进制数字占据4个空格,我仍然对如何实现它感到困惑。此外,我不知道检索数字长度的好方法。
这可以很容易地用字符串来完成,但是我需要在数千次迭代的上下文中使用它,并且认为选择字符串不是一个好主意。
那么,有什么好方法可以在没有字符串的情况下删除呢?
与您描述的想法类似,这可以通过为上部和下部创建遮罩、移动上部,然后重新组装来实现。
int remove(int x, int i) {
// create a mask covering the highest 1-bit and all lower bits
int m = x;
m |= (m >>> 1);
m |= (m >>> 2);
m |= (m >>> 4);
m |= (m >>> 8);
m |= (m >>> 16);
// clamp to 4-bit boundary
int l = m & 0x11111110;
m = l - (l >>> 4);
// shift to select relevant position
m >>>= 4 * i;
// assemble result
return ((x & ~(m << 4)) >>> 4) | (x & m);
}
其中">>>"是一个无符号移位。
需要注意的是,如果0表示独立于输入的32位单词中的最高十六进制数字,这要简单得多:
int remove(int x, int i) {
int m = 0xffffffff >>> (4*i);
return ((x & ~m) >>> 4) | (x & (m >>> 4));
}
解决方案:
将使用10的操作替换为使用16的操作。
Demo
使用位运算符:
public class Main {
public static void main(String[] args) {
int n = 0x12345;
int temp = n;
int length = 0;
// Find length
while (temp != 0) {
length++;
temp /= 16;
}
System.out.println("Length of the number: " + length);
// Remove digit at index 3
int m = n;
int index = 3;
for (int i = index + 1; i <= length; i++) {
m /= 16;
}
m *= 1 << ((length - index - 1) << 2);
m += n % (1 << ((length - index - 1) << 2));
System.out.println("The number after removing digit at index " + index + ": 0x" + Integer.toHexString(m));
}
}
输出:
Length of the number: 5
The number after removing digit at index 3: 0x1235
使用Math::pow
:
public class Main {
public static void main(String[] args) {
int n = 0x12345;
int temp = n;
int length = 0;
// Find length
while (temp != 0) {
length++;
temp /= 16;
}
System.out.println("Length of the number: " + length);
// Remove digit at index 3
int m = n;
int index = 3;
for (int i = index + 1; i <= length; i++) {
m /= 16;
}
m *= ((int) (Math.pow(16, length - index - 1)));
m += n % ((int) (Math.pow(16, length - index - 1)));
System.out.println("The number after removing digit at index " + index + ": 0x" + Integer.toHexString(m));
}
}
输出:
Length of the number: 5
The number after removing digit at index 3: 0x1235
JavaScript版本:
n = parseInt(12345, 16);
temp = n;
length = 0;
// Find length
while (temp != 0) {
length++;
temp = Math.floor(temp / 16);
}
console.log("Length of the number: " + length);
// Remove digit at index 3
m = n;
index = 3;
for (i = index + 1; i <= length; i++) {
m = Math.floor(m / 16);
}
m *= 1 << ((length - index - 1) << 2);
m += n % (1 << ((length - index - 1) << 2));
console.log("The number after removing digit at index " + index + ": 0x" + m.toString(16));
这是通过编写一个从右边移除的方法,但调整参数从左边移除来实现的。额外的好处是,从右边移除也可以使用。此方法使用longs
来最大化十六进制值的长度。
long n = 0x12DFABCA12L;
int r = 3;
System.out.println("Supplied value: " + Long.toHexString(n).toUpperCase());
n = removeNthFromTheRight(n, r);
System.out.printf("Counting %d from the right: %X%n", r, n);
n = 0x12DFABCA12L;
n = removeNthFromTheLeft(n, r);
System.out.printf("Counting %d from the left: %X%n", r, n);
打印
Supplied value: 12DFABCA12
Counting 3 from the right: 12DFABA12
Counting 3 from the left: 12DABCA12
这是通过递归地从末尾删除一个数字,直到您想要删除的数字之前。然后删除它并通过调用堆栈返回,用原始值重建数字。
此方法从右侧开始计数。
public static long removeNthFromTheRight(long v, int n) {
if (v <= 0) {
throw new IllegalArgumentException("Not enough digits");
}
// save hex digit
long k = v % 16;
while (n > 0) {
// continue removing digit until one
// before the one you want to remove
return removeNthFromTheRight(v / 16, n - 1) * 16 + k;
}
if (n == 0) {
// and ignore that digit.
v /= 16;
}
return v;
}
此方法从左起计数。它只是简单地调整n
的值,然后调用removeFromTheRight
。
public static long removeNthFromTheLeft(long v, int n) {
ndigits = (67-Long.numberOfLeadingZeros(v))>>2;
// Now just call removeNthFromTheRight with modified paramaters.
return removeNthFromTheRight(v, ndigits - n - 1);
}
这是我的版本使用位操作和解释。
最高设置位有助于找到掩码的偏移量。对于
long
,该位是64,即前导零的数目。要得到十六进制数字的数目,必须除以4。要计算出可被4整除的数字,必须先加3再除法。因此,数字的数量:digits = (67-Long.numberOfLeadingZeros(i))>>2
然后需要对其进行调整,以掩盖数字的适当部分。offset = digits-i - 1
CCD_ 21是屏蔽待去除的数字的屏蔽。因此,从
-1L (all hex 'F')
和右移4*(16-offset)
位开始。这将产生一个遮罩,遮罩要移除的数字右侧的所有内容。注意:如果offset
是0
,则移位运算符将是64
,并且不会移位任何位。为了适应这种情况,将轮班操作分解为两个操作。现在只需屏蔽低阶位
v & m
- 并且高阶比特对CCD_ 28比特进行右移以消除期望的数字。
(v>>>4)^ ~m
- 然后将这两部分简单地进行OR运算
static long remove(long v, int i) {
int offset = ((67 - Long.numberOfLeadingZeros(v))>>2) - i - 1;
long m = (-1L >>> (4*(16 - offset) - 1)) >> 1;
return ((v >>> 4) & ~m) | (v & m);
}