我有一个随机生成器,它生成从-2 ^ 63到2 ^ 63的随机数,而不是java.util.Random。
我需要在(0,1)中生成随机双精度,这是我到目前为止所做的:
return (seed/(double)(9223372036854775807L))/2+0.5;//seed is a random long
这是对的吗?有没有数值问题(下溢?)?
可能更好/更快?
谢谢。
答案 0 :(得分:1)
我更愿意只看到一个部门。
0.5+(seed/1.84467440737096E+19);
那就是说,你将遇到浮点精度问题,因为你有64个随机整数位,然后尝试将其压缩到53位双精度。你可能最好为浮点值制作一个专用的生成器,但我不能肯定地说,因为我不知道你的动机。
答案 1 :(得分:1)
我会使用Math.scalb作为最有效的,并确保由于舍入或表示错误而没有有趣的行为
double d = Math.scalb(seed >>> 1, -63);
你只能在一个双精度中使用53位,所以有些会被丢弃。
如果你跑
long seed = Long.MAX_VALUE;
System.out.println(Math.scalb(seed >>> 1, -63));
打印
0.5
如果种子为0,则得到0.0
种子为-1,你得到1.0
答案 2 :(得分:1)
最快的方法可能就是将long中的前三位设置为0
,然后使用这些位进行加倍。:
double rand = Double.longBitsToDouble(seed & 0x1FFFFFFFFFFFFFFFL);
这可以通过强制符号为正,指数小于0来实现,这将导致尾数至少向右移动一次。假设长的所有整数都是完全随机的,它给出了均匀分布。这是一个完整的Java程序,它使用Random生成随机long,然后这个方法将它们转换为0到1之间的double:
import java.util.Random;
class Main{
public static void main(String[] args){
Random rand = new Random();
long seed = rand.nextLong();
double x = Double.longBitsToDouble(seed & 0x1FFFFFFFFFFFFFFFL);
System.out.println(x);
}
}
这是10次执行的输出:
1.1211565592484309E-247
8.84224349357039E-242
6.956043405745214E-271
3.747746366809532E-232
9.302628573486166E-158
1.1440116527034282E-166
1.2574577719255876E-198
5.104999671234867E-269
3.360619724894072E-213
1.5654452507283312E-220
修改强>
这使得所有可能的双精度在0和1之间均匀分布。由于还有更多的小双打,你可能永远不会看到接近1的数字。你可以通过根据数字的位生成一个新的指数来解决这个问题。现有的,但你需要一个循环来做它,所以它可能不是考虑到这个因素后最快的方法:
long exponent = 0;
for(int i = 52; (seed >>> i & 1) > 0; i++) exponent++;
double x = Double.longBitsToDouble(seed & 0x000FFFFFFFFFFFFFL | ((1022 - exponent) << 52));
0.4773960377161338
0.929045618651037
0.7183096209363845
0.33962049395497845
0.45568660174922454
0.11670190555677815
0.09371618427480996
0.8192870898479095
0.9365016017283178
0.11311614413193898
答案 3 :(得分:0)
不完全是。我认为更简单的方法是执行以下操作:
new Random(seed).nextDouble()
答案 4 :(得分:0)
除非我误读你需要从0到1的随机双精度,否则Java内置的Math.random就是这样。因此,您可以避免目前正在进行的所有转换。