最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

random - 48-bit bitwise operations in Javascript? - Stack Overflow

programmeradmin0浏览0评论

I've been given the task of porting Java's Java.util.Random() to JavaScript, and I've run across a huge performance hit/inaccuracy using bitwise operators in Javascript on sufficiently large numbers. Some cursory research states that "bitwise operators in JavaScript are inherently slow," because internally it appears that JavaScript will cast all of its double values into signed 32-bit integers to do the bitwise operations (see here for more on this.) Because of this, I can't do a direct port of the Java random number generator, and I need to get the same numeric results as Java.util.Random(). Writing something like

  this.next = function(bits) {
    if (!bits) {
       bits = 48;
    }
    this.seed = (this.seed * 25214903917 + 11) & ((1 << 48) - 1);
    return this.seed >>> (48 - bits);
  };

(which is an almost-direct port of the Java.util.Random()) code won't work properly, since Javascript can't do bitwise operations on an integer that size.)

I've figured out that I can just make a seedable random number generator in 32-bit space using the Lehmer algorithm, but the trick is that I need to get the same values as I would with Java.util.Random(). What should I do to make a faster, functional port?

I've been given the task of porting Java's Java.util.Random() to JavaScript, and I've run across a huge performance hit/inaccuracy using bitwise operators in Javascript on sufficiently large numbers. Some cursory research states that "bitwise operators in JavaScript are inherently slow," because internally it appears that JavaScript will cast all of its double values into signed 32-bit integers to do the bitwise operations (see here for more on this.) Because of this, I can't do a direct port of the Java random number generator, and I need to get the same numeric results as Java.util.Random(). Writing something like

  this.next = function(bits) {
    if (!bits) {
       bits = 48;
    }
    this.seed = (this.seed * 25214903917 + 11) & ((1 << 48) - 1);
    return this.seed >>> (48 - bits);
  };

(which is an almost-direct port of the Java.util.Random()) code won't work properly, since Javascript can't do bitwise operations on an integer that size.)

I've figured out that I can just make a seedable random number generator in 32-bit space using the Lehmer algorithm, but the trick is that I need to get the same values as I would with Java.util.Random(). What should I do to make a faster, functional port?

Share Improve this question edited Apr 4, 2010 at 19:06 James Kolpack 9,3822 gold badges45 silver badges60 bronze badges asked Apr 4, 2010 at 18:56 randomhelprandomhelp 812 bronze badges
Add a ment  | 

5 Answers 5

Reset to default 4

Instead of foo & ((1 << 48) - 1) you should be able to use foo % Math.pow(2,48).

All numbers in Javascript are 64-bit floating point numbers, which is sufficient to represent any 48-bit integer.

Bear in mind that a bit shift is directly equivalent to a multiplication or division by a power of 2.

1 << x == 1 * Math.pow(2,x)

It is slower than bit shifting, but allows you to extend beyond 32 bits. It may be a faster solution for bits > 32, once you factor in the additional code you need to support higher bit counts, but you'll have to do some profiling to find out.

48-bit bitwise operations are not possible in JavaScript. You could use two numbers to simulate it though.

Be aware that this.seed * 25214903917 will in the worst case need 48+39 bits of precision, which exceeds the precision of JavaScript's number type (which has 53 bits of precision).

Since BigInt was introduced in JavaScript, you can now do this with primitive types without loss of precision.

Here is a subset of Java's Random class implemented in JavaScript, with a small demo following:

class JavaRandom {
    constructor() {
        this.setSeed(Math.floor(Math.random() * 0x1000000000000));
    }
    setSeed(seed) {
        this.seed = (BigInt(seed) ^ 0x5DEECE66Dn) & 0xFFFFFFFFFFFFn;
    }
    next(bits=48) {
        this.seed = (this.seed * 0x5DEECE66Dn + 0xBn) & 0xFFFFFFFFFFFFn;
        return Number(this.seed >> BigInt(48 - bits));
    }
    nextInt() {
        return this.next(32) | 0;
    }
    nextDouble() {
        return (this.next(26) * 0x8000000 + this.next(27)) / 0x20000000000000;
    }
}

// Demo
const rnd = new JavaRandom();
rnd.setSeed(12345);
for (let i = 0; i < 10; i++) {
    console.log(rnd.nextDouble());
} 

This outputs:

0.3618031071604718
0.932993485288541
0.8330913489710237
0.32647575623792624
0.2355237906476252
0.34911535662488336
0.4480776326931518
0.6381529437838686
0.1582665432952023
0.768888060192009

Which is the exact same output produced by the following Java program:

import java.util.*;

class Main {
    public static void main(String[] args) {
        var rnd = new Random();
        rnd.setSeed(12345);
        for (int i = 0; i < 10; i++) {
            System.out.println(rnd.nextDouble());
        }
    }
}

An alternative is to use a boolean array of 48 booleans, and implement the shifting yourself. I don't know if this is faster, though; but I doubt it, since all booleans are stored as doubles.

发布评论

评论列表(0)

  1. 暂无评论