如何在不使用字符串的情况下删除整数的第n个十六进制数字



考虑一个十六进制整数值,如n = 0x12345,如何通过执行remove(n, 3)(big-endian(来获得0x1235作为结果?

对于上面的输入,我认为这可以通过执行一些比特步骤来实现:

  • partA=从索引0targetIndex - 1提取部分(应返回0x123(
  • partB=从targetIndex + 1length(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)位开始。这将产生一个遮罩,遮罩要移除的数字右侧的所有内容。注意:如果offset0,则移位运算符将是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);
}

最新更新