Skip to content

Commit f77e284

Browse files
committed
refactor: align with Node's behavior for randomInt()
1 parent 5ce48b2 commit f77e284

2 files changed

Lines changed: 32 additions & 18 deletions

File tree

src/randomInt.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { assertIsInteger } from './assertion/assertIsInteger.js';
1+
import { assertIsSafeInteger } from './assertion/assertIsSafeInteger.js';
22
import { isObject } from './predicate/isObject.js';
33

44
import type { RequireAtLeastOne } from './types/records.js';
@@ -18,20 +18,26 @@ export function randomInt(options?: RandomIntOptions): number;
1818
export function randomInt(min: number, max?: number): number;
1919

2020
export function randomInt(arg1?: number | RandomIntOptions, arg2?: number): number {
21-
const { min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER } = isObject(arg1)
22-
? arg1
23-
: { min: arg1, max: arg2 };
21+
const { min = 0, max = Number.MAX_SAFE_INTEGER } = isObject(arg1) ? arg1 : { min: arg1, max: arg2 };
2422

25-
assertIsInteger(min, 'min must be an integer');
26-
assertIsInteger(max, 'max must be an integer');
23+
assertIsSafeInteger(min, 'min must be a safe integer');
24+
assertIsSafeInteger(max, 'max must be a safe integer');
2725

28-
if (min === max) {
29-
return min;
30-
}
31-
32-
if (min > max) {
26+
if (min >= max) {
3327
throw new RangeError('min must be less than max');
3428
}
3529

36-
return Math.floor(Math.random() * (max - min)) + min;
30+
const minBigInt = BigInt(min);
31+
const range = BigInt(max) - minBigInt;
32+
const limit = (2n ** 64n / range) * range;
33+
const data = new BigInt64Array(1);
34+
35+
let value: bigint;
36+
37+
do {
38+
crypto.getRandomValues(data);
39+
value = BigInt.asUintN(64, data[0]!);
40+
} while (value >= limit);
41+
42+
return Number(minBigInt + (value % range));
3743
}

test/randomInt.test.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ import { describe, expect, it } from 'vitest';
33
import { randomInt } from '../src/randomInt.js';
44

55
describe('randomInt()', () => {
6-
it('Returns early when min and max are the same', () => {
7-
expect(randomInt(0, 0)).toEqual(0);
8-
});
9-
106
it('Returns a random integer', () => {
117
const value = randomInt(100, 200);
128

@@ -27,16 +23,28 @@ describe('randomInt()', () => {
2723
expect(value).lessThan(1000);
2824
});
2925

30-
it('Defaults to SAFE_INTEGER range', () => {
26+
it('Defaults to [0, MAX_SAFE_INTEGER) range', () => {
3127
const value = randomInt();
3228

33-
expect(value).greaterThanOrEqual(Number.MIN_SAFE_INTEGER);
29+
expect(value).greaterThanOrEqual(0);
3430
expect(value).lessThan(Number.MAX_SAFE_INTEGER);
3531
});
3632

33+
it('Throws when min and max are equal', () => {
34+
expect(() => {
35+
randomInt(0, 0);
36+
}).toThrow();
37+
});
38+
3739
it('Throws when min > max', () => {
3840
expect(() => {
3941
randomInt(100, 0);
4042
}).toThrow();
4143
});
44+
45+
it('Throws when range is too large', () => {
46+
expect(() => {
47+
randomInt(Number.MIN_VALUE, Number.MAX_VALUE);
48+
}).toThrow();
49+
});
4250
});

0 commit comments

Comments
 (0)