32位无符号整数的反转位

时间:2016-09-24 14:37:52

标签: java bit-manipulation computer-science

问题是要反转32位无符号整数的位(因为Java没有我们长时间使用的无符号整数)。

以下是我的代码的两个版本。我有两个问题:

(1)为什么我的第一和第二个解决方案不会返回相同的值(正确与否)

(2)我的第一和第二个解决方案在没有得到正确答案时出错了

    //reverse(3) returns 0
    public static long reverse(long a) {
        long numBits = 32;
        long finalResult = 0;
        for(int i = 0; i < numBits; i++){
            long ithBit = a & (1 << i);
            finalResult = finalResult + ithBit * (1 << (numBits - i - 1));
        }
        return finalResult;
    }

第二版:

    //reverse(3) return 4294967296
    public static long reverse(long a) {
        long numBits = 32L;
        long finalResult = 0L;
        for(long i = 0L; i < numBits; i++){
            long ithBit = a & (1L << i);
            finalResult = finalResult + ithBit * (1L << (numBits - i - 1L));
        }
        return finalResult;
    }

此代码(解决方案)返回正确答案:

    //reverse(3) returns 3221225472
    public static long reverse(long A) {
        long rev = 0;

        for (int i = 0; i < 32; i++) {
            rev <<= 1;
            if ((A & (1 << i)) != 0)
                rev |= 1;
        }

        return rev;

    }

谢谢!

4 个答案:

答案 0 :(得分:1)

  

因为Java没有无符号整数,所以我们使用很长时间。

这通常是不必要的,因为除了除法和比较之外的所有算术运算都会导致java使用two's complement representation中的无符号和有符号数的相同位模式。对于后两个操作Integer.divideUnsigned(int, int)Integer.compareUnsigned(int, int)可用。

  

问题是要反转32位无符号整数的位

Integer.reverse(int)

Relevant docs,强烈建议花一些时间阅读它们。

答案 1 :(得分:0)

在这两个版本中,您都遇到了逻辑问题:

ithBit * (1 << (numBits - i - 1));

因为两个数字相乘于设置的第31位(第32位)的相同位模式。

在版本1中,如果设置了位-2^31,则添加的数量为0,因为您正在对int进行位移,因此位移结果为int表示-2^31表示高位被设置,或2^31表示每个其他位,这可能是由于自动转换为结果的长度。您有两种位(0和非0),因此结果为零。

版本2存在同样的问题,但没有消极的int问题,因为您正在移动long。每个1位将添加2^31。数字3设置了2位,因此您的结果为2 * 2^31(或2^324294967296

要修复您的逻辑,请使用版本2,但请将乘以ithBit

答案 2 :(得分:0)

让您在迭代时查看您的值。为了澄清,我们将查看中间值,因此我们将代码更改为:

int n = (1 << (numBits - i - 1));
long m = ithBit * n;
finalResult = finalResult + m;

您的起始值是3:

a = 0000 0000 0000 0000 0000 0000 0000 0011

第一次循环迭代(i = 0):

ithBit      = 00000000 00000000 00000000 00000001
n           = 11111111 11111111 11111111 11111111 10000000 00000000 00000000 00000000
m           = 11111111 11111111 11111111 11111111 10000000 00000000 00000000 00000000
finalResult = 11111111 11111111 11111111 11111111 10000000 00000000 00000000 00000000

第二次循环迭代(i = 1):

ithBit      = 00000000 00000000 00000000 00000010
n           = 01000000 00000000 00000000 00000000
m           = 10000000 00000000 00000000 00000000
finalResult = 00000000 00000000 00000000 00000000

如您所见,第一个迭代设置n = 1 << 31,即-2147483648。在您的第二个版本中,您执行n = 1L << 31,即2147483648,这就是为什么您的两个版本会产生不同结果的原因。

正如您所看到的,您绝对想要m = ithBit * n部分。

通过自己打印来查看您的号码,然后您就可以了解它。

顺便说一下,这是我的版本。如果您无法理解它,请尝试打印中间值以查看正在进行的操作。

public static long reverse4(long a) {
    long rev = 0;
    for (int i = 0; i < 32; i++, a >>= 1)
        rev = (rev << 1) | (a & 1);
    return rev;
}

答案 3 :(得分:0)

你的两个例子的问题是“第i位”不是0或1而是掩盖了。在任何一种情况下,第31位都是0x8000_0000。在第一种情况下,这是一个int,所以它是负的,当转换为long时它保持为负。在第二种情况下,它已经很长,所以它保持正面。要修复它以实现您的预​​期,请执行以下操作:

 ithBit = (a >>> i) & 1;

顺便说一句,使用long是愚蠢的;只要您了解Java中有两种类型的转换,unsigned vs. signed没有任何区别。

顺便说一下,这三个例子都很糟糕。如果你正在做位操作,你想要速度,对吗? (为什么还要打扰位?)

这是如何正确行事(不是我的,从http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits偷来的):

a = ((a >>> 1) & 0x55555555) | ((a & 0x55555555) << 1);
a = ((a >>> 2) & 0x33333333) | ((a & 0x33333333) << 2);
a = ((a >>> 4) & 0x0F0F0F0F) | ((a & 0x0F0F0F0F) << 4);
a = ((a >>> 8) & 0x00FF00FF) | ((a & 0x00FF00FF) << 8);
a = ( a >>> 16             ) | ( a               << 16);