Java 中的无符号右移(将正 long 移位 32 位会得到负整数)

问题描述 投票:0回答:1

我有以下问题。

我有一个生成长数字的 PRNG 代码:

private static long next() {
    // PRNG code goes here
}

我还有一个名为

nextLong()
的方法,可以确保返回值为正:

public static long nextLong() {
    return next() >>> 1;
}

我还有以下代码,返回 [0; 范围内的整数。整数.MAX_VALUE]:

public static int nextInt() {
    int result = savedInt;
    if (result < 0) {
        long l = nextLong();
        savedInt = (int) (l >> 32);
        return (int) (l  & ~(1 << 31));
    }
    savedInt = -1;
    return result;
}

这个想法是,由于

longInt()
返回 63 个随机位,而我只需要 31 位作为正整数,因此我将返回较低的 31 位并保存较高的 32 位以在下次调用该方法时返回它们(而不是再次运行昂贵的伪随机数生成)。

我正在代码中进行有符号右移:

savedInt = (int) (l >> 32);

原因是,如果我执行无符号右移(>>>),有时会得到负整数并导致单元测试失败。这对我来说似乎非常奇怪,因为

nextLong()
返回的 64 位值的最高位已经为零,所以如果我将其向右移动 32 位,则结果的最高位32 位值应该为零,对吧?

这是我用来对我的

nextInt()
方法进行单元测试的代码:

@Test
void nextIntTest() {
    int k, j;
    for (int i = 0; i < NUM_ITERATIONS; i++) {
        j = MyCustomRandom.nextInt();
        k = MyCustomRandom.nextInt();
        assertFalse(j == k);
        assertTrue(k >= 0 && j >= 0);
    }
} 

(我知道这对于 PRNG 来说不是一个好的测试,我使用这些简单的测试只是为了确保我不会做任何明显愚蠢的事情。)

所以我的问题是:在这种情况下我做错了什么?我的意思是,当我使用有符号右移时,我很满意我的代码或多或少可以正确运行,但是为什么无符号右移会产生意外的结果?

更新

在尝试找到一个正值

l
导致
(int) (l >>> 32)
为负值之后,我没有成功。事实上,我编写了以下代码以便能够检查我的类的内部状态:

    long l;
    int k;
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
        l = MyCustomRandom.nextLong();
        k = (int) (l >>> 32);
        if (k < 0) {
            System.out.println("gotcha!");
        }
    }

并在

System.out.println()
行设置断点。正如我所料,连续尝试了大约15分钟后,断点从未触发。但是,如果我设置以下单元测试:

@Test
void RightShiftTest() {
    long l;
    int k;
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
        l = MyCustomRandom.nextLong();
        k = (int) (l >>> 32);
        assertTrue (k >= 0);
    }
}

测试大约每第二次或第三次运行就会失败。这让我怀疑还有其他事情发生。

更新2 原来,有问题的机器有一个非常旧的(并且长期废弃的)Oracle JDK8版本,升级到最新的Temurin jdk8u382后问题就消失了

java bit-manipulation bit-shift
1个回答
0
投票

问题就在这里。

给定一个

long
,如果设置了
high order bit
中的
low order int
(也称为符号位),则当转换为 int 时,所得的
int
将为负数。即使使用了
>>>
,设置该位也可能是由于长右移而导致的。

long v = (long)Math.pow(2,32)-1; // contains the largest unsigned int value
System.out.println(v);  //prints 4294967295

int i = (int)v;
System.out.println(i);  // prints -1

这是两个值之间的交替值的示例,一个负值和一个正值,因为长整型每次迭代右移 1 位。

long v = 0b010101010101010101010101010101010101010101010101L;

for(int i = 0; i < 10;i++) {
    int iCast = (int)v;
    System.out.println(iCast);
    v>>>=1;
}

打印

1431655765
-1431655766
1431655765
-1431655766
1431655765
-1431655766
1431655765
-1431655766
1431655765
-1431655766


© www.soinside.com 2019 - 2024. All rights reserved.