我有以下问题。
我有一个生成长数字的 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后问题就消失了
问题就在这里。
给定一个
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