How Lodash _.random Generates Integers and Floats
The _.random method in the Lodash JavaScript library
provides a unified interface for generating both pseudo-random integers
and floating-point values within an inclusive range. By analyzing the
provided arguments, Lodash automatically determines whether the output
should be an integer or a decimal, utilizing JavaScript's native
Math.random() engine under the hood alongside specific
arithmetic adjustments to guarantee inclusive bounds.
Argument Normalization and Detection
Lodash accepts up to three arguments:
_.random([lower=0], [upper=1], [floating]). Before
computing a value, the function normalizes the input:
- Default Bounds: If no arguments are passed, it
defaults to
lower = 0andupper = 1. If only one value is provided, it assigns that value toupperand setslowerto0. - Inverted Ranges: If
loweris greater thanupper, the method swaps their values to maintain a valid interval. - Floating-Point Inference: Lodash checks whether a
floating-point result is required. A result is treated as floating-point
if:
- The
floatingboolean parameter is explicitly set totrue. - Either
lowerorupperis already a floating-point number (detected using modulus arithmetic, such aslower % 1 !== 0orupper % 1 !== 0).
- The
Integer Generation
When generating integers (the default behavior when passing whole
numbers without setting floating to true),
Lodash produces an evenly distributed integer between lower
and upper, inclusive.
The calculation uses Math.floor() alongside an
offset:
lower + Math.floor(Math.random() * (upper - lower + 1))Because Math.random() returns a number in the half-open
interval [0, 1), multiplying by
(upper - lower + 1) scales the range. Applying
Math.floor() maps the continuous float into distinct
integer buckets ranging from 0 to
upper - lower. Adding lower shifts the result
back to the desired lower bound, ensuring that upper has an
equal probability of being selected.
Floating-Point Generation
When the floating-point path is triggered, Lodash avoids
Math.floor() to preserve fractional precision. Instead of
using a standard continuous range calculation that excludes the upper
boundary, Lodash implements a precision offset so that
upper remains theoretically attainable:
const rand = Math.random();
const randLength = `${rand}`.length - 1;
return Math.min(
lower + (rand * (upper - lower + parseFloat(`1e-${randLength}`))),
upper
);By computing a small fraction based on the string length of the
random decimal (1e-randLength), Lodash expands the upper
limit slightly past upper before clamping the result with
Math.min(..., upper). This compensates for the fact that
Math.random() never naturally reaches 1.0,
ensuring the entire requested range remains inclusive without
overflowing the upper bound.