@alisey/pcg32
Advanced tools
Comparing version 1.0.0 to 1.1.0
const wasmBuffer = new Uint8Array([ | ||
0,97,115,109,1,0,0,0,1,10,2,96,0,1,127,96,1,127,1,127,3,3,2,0,1,6,28,2,126,1,66,155,213,191,164,231,188,146,158,133,127,11,126,1,66,219,183,229,165,185,185,142,159,90,11,7,45,4,5,115,116,97,116,101,3,0,8,115,101,113,117,101,110,99,101,3,1,10,114,97,110,100,111,109,66,105,116,115,0,0,9,114,97,110,100,111,109,73,110,116,0,1,10,78,2,44,1,1,126,35,0,34,0,66,173,254,213,228,212,133,253,168,216,0,126,35,1,124,36,0,32,0,66,18,136,32,0,133,66,27,136,167,32,0,66,59,136,167,120,11,31,1,2,127,65,0,32,0,107,32,0,112,33,2,3,64,16,0,34,1,32,2,73,13,0,11,32,1,32,0,112,11 | ||
0,97,115,109,1,0,0,0,1,10,2,96,0,1,127,96,1,127,1,127,3,3,2,0,1,6,28,2,126,1,66,155,213,191,164,231,188,146,158,133,127,11,126,1,66,219,183,229,165,185,185,142,159,90,11,7,46,4,5,115,116,97,116,101,3,0,8,115,101,113,117,101,110,99,101,3,1,11,114,97,110,100,111,109,73,110,116,51,50,0,0,9,114,97,110,100,111,109,73,110,116,0,1,10,78,2,44,1,1,126,35,0,34,0,66,173,254,213,228,212,133,253,168,216,0,126,35,1,124,36,0,32,0,66,18,136,32,0,133,66,27,136,167,32,0,66,59,136,167,120,11,31,1,2,127,65,0,32,0,107,32,0,112,33,2,3,64,16,0,34,1,32,2,73,13,0,11,32,1,32,0,112,11 | ||
]); | ||
@@ -8,3 +8,3 @@ const wasmModule = new WebAssembly.Module(wasmBuffer); | ||
const wasmSequence = wasmInstance.exports.sequence; | ||
const wasmRandomBits = wasmInstance.exports.randomBits; | ||
const wasmRandomInt32 = wasmInstance.exports.randomInt32; | ||
const wasmRandomInt = wasmInstance.exports.randomInt; | ||
@@ -15,21 +15,46 @@ | ||
*/ | ||
export const randomBits = () => wasmRandomBits() >>> 0; | ||
export const randomInt32 = () => wasmRandomInt32() >>> 0; | ||
/** | ||
* Returns a uniformly distributed 32-bit unsigned random integer in the | ||
* range [0, bound). | ||
* You may think that you can just run randomBits() % bound, but doing so | ||
* introduces nonuniformity when bound is not a power of two. | ||
* Returns a uniformly distributed 32-bit unsigned random integer in the range | ||
* [0, bound). | ||
*/ | ||
export const randomInt = (bound) => wasmRandomInt(bound) >>> 0; | ||
export const randomInt = (bound) => { | ||
if (bound < 1 || bound > 0x1_0000_0000) { | ||
throw new RangeError('randomInt() bound must be between 1 and 2³²'); | ||
} else if (bound === 0x1_0000_0000) { | ||
return wasmRandomInt32() >>> 0; | ||
} | ||
return wasmRandomInt(bound) >>> 0; | ||
}; | ||
/** | ||
* Returns a floating point number in the range [0, 1) that has been rounded | ||
* down to the nearest multiple of 2⁻³². | ||
* Returns a uniformly distributed floating point number in the range [0, 1) | ||
* that has been rounded down to the nearest multiple of 2⁻⁵³. | ||
* | ||
* This includes all possible double-precision floating-point numbers in the | ||
* range [0.5, 1), but only half of numbers in the range [0.25, 0.5), a quarter | ||
* of numbers in the range [0.125, 0.25), and so forth, because the floating | ||
* point format has a higher resolution for numbers closer to zero. This is also | ||
* the reason why selecting a number from all floating point numbers in the | ||
* range [0, 1) with equal probability would not result in a uniform | ||
* distribution. | ||
* | ||
* Multiplying the result by 2⁵³ can be used to generate a random integer in the | ||
* range [0, 2⁵³), but for other ranges of integers it's preferable to use | ||
* randomInt() because it's more efficient and doesn't introduce a bias due to | ||
* rounding when the upper bound is not a power of two. | ||
*/ | ||
export const random = () => randomBits() * 2 ** -32; | ||
export const random = () => | ||
// 1. Create a 53-bit integer from two 32-bit integers by combining the | ||
// lowest 21 bits of the first with all bits of the second. | ||
// 2. Multiply the result by 2⁻⁵³ to map it to the range [0, 1). | ||
// 0x0000_0000, 0x0000_0000 ⇒ 0 | ||
// 0x0000_0000, 0x0000_0001 ⇒ 2⁻⁵³ | ||
// 0x001f_ffff, 0xffff_ffff ⇒ 1 - 2⁻⁵³ | ||
((randomInt32() & 0x1f_ffff) * 0x1_0000_0000 + randomInt32()) * 1.1102230246251565e-16; | ||
/** | ||
* Updates the internal state of the generator. The generator has 2⁶⁴ possible | ||
* internal states, and iterates through all of them. | ||
* Updates the internal state of the generator, which has 2⁶⁴ possible internal | ||
* states, and iterates through all of them. | ||
* @param state - a 64-bit unsigned BigInt representing the new state. | ||
@@ -64,1 +89,13 @@ */ | ||
export const getSequence = () => BigInt.asUintN(64, wasmSequence.value); | ||
/** | ||
* Seeds the generator. If the value is not provided, uses a value based on | ||
* Math.random(). | ||
* @param value - a 64-bit BigInt. | ||
*/ | ||
export const seed = (value = BigInt(Math.floor(Math.random() * 2 ** 53))) => { | ||
wasmState.value = 0n; | ||
randomInt32(); | ||
wasmState.value += value; | ||
randomInt32(); | ||
}; |
{ | ||
"name": "@alisey/pcg32", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"author": "Alexey Lebedev", | ||
@@ -5,0 +5,0 @@ "description": "PCG-32 random number generator implemented in WebAssembly", |
# PCG-32: A Seedable 32-bit PRNG | ||
A WebAssembly port of the PCG Random Number Generator, [Minimal C Edition](https://github.com/imneme/pcg-c-basic). It's slightly slower than `Math.random()` in V8, and provides only 32 bits of randomness instead of 52. | ||
A WebAssembly port of the PCG Random Number Generator, [Minimal C Edition](https://github.com/imneme/pcg-c-basic). | ||
@@ -8,10 +8,8 @@ ## Usage | ||
```js | ||
import * as pcg32 from '@alisey/pcg32'; | ||
import * as pcg from '@alisey/pcg32'; | ||
pcg32.setState(0x0123456789ABCDEFn); | ||
pcg.setState(0x0123456789ABCDEFn); | ||
console.log(pcg32.randomBits()); // ⇒ 610837995 | ||
console.log(pcg32.randomInt(10)); // ⇒ 7 | ||
console.log(pcg32.random()); // ⇒ 0.24794234661385417 | ||
console.log(pcg32.getState()); // ⇒ 4259798932287663464n | ||
console.log(pcg.randomInt(10)); // ⇒ 5 | ||
console.log(pcg.random()); // ⇒ 0.3697000732146962 | ||
``` | ||
@@ -21,17 +19,13 @@ | ||
##### `randomBits(): number` | ||
Returns a uniformly distributed 32-bit unsigned random integer. | ||
##### `randomInt(bound: number): number` | ||
Returns a uniformly distributed 32-bit unsigned random integer in the range | ||
[0, bound). | ||
[0, bound), where `bound` is a number from 1 to 2³². | ||
##### `random(): number` | ||
Returns a floating point number in the range [0, 1) that has been rounded down | ||
to the nearest multiple of 2⁻³². | ||
Returns a uniformly distributed floating point number in the range [0, 1) that | ||
has been rounded down to the nearest multiple of 2⁻⁵³. | ||
##### `setState(state: BigInt)` | ||
##### `setState(state: bigint): void` | ||
@@ -42,3 +36,3 @@ Updates the internal state of the generator. The generator has 2⁶⁴ possible | ||
##### `getState(): BigInt` | ||
##### `getState(): bigint` | ||
@@ -48,2 +42,16 @@ Returns a 64-bit unsigned BigInt representing the internal state of the | ||
##### `seed(value?: bigint): void` | ||
Seeds the generator with a 64-bit BigInt. If the value is not provided, uses | ||
a value based on `Math.random()`. | ||
## Performance | ||
As of 2021, in V8 `pcg.randomInt()` is as fast as | ||
`Math.floor(Math.random() * n)`, but doesn't introduce bias. `pcg.random()` is | ||
2 times slower than `Math.random()`. | ||
In SpiderMonkey and JavaScriptCore both functions are 10 times slower than | ||
native equivalents. | ||
## Working With This Repo | ||
@@ -50,0 +58,0 @@ |
7891
92
64