Lodash _.random Floating-Point Boundary Logic
Lodash's _.random function determines whether to return
an integer or a floating-point number through a combination of explicit
boolean flags, parameter shifting, and modulo checks on boundary values.
When floating-point mode is engaged, the library alters its mathematical
model from a discrete uniform distribution to an extended continuous
distribution clamped by a minimum boundary function. This article breaks
down the internal parameter evaluation, the floating-point detection
logic, and the mathematical mechanics that dictate how floating
boundaries and edge-case distributions are handled in Lodash.
Parameter Normalization and Boolean Coercion
The _.random function accepts arguments dynamically:
_.random([lower=0], [upper=1], [floating]). To determine
the target distribution, Lodash first normalizes its inputs to resolve
flexible argument signatures.
If only one argument is provided and it is a boolean, or if the
second parameter is a boolean, Lodash shifts variables so that
floating receives that truthy or falsy value. If
floating is omitted entirely, Lodash infers whether
floating-point logic is required by running a modulo check on the
boundaries:
floating || lower % 1 || upper % 1If either boundary is not a whole number (i.e.,
boundary % 1 !== 0), the function automatically coerces the
calculation into floating-point mode, regardless of whether a boolean
flag was explicitly passed.
Discrete vs. Continuous Distribution Branches
The function diverges into two separate execution branches depending on the boolean state of the floating condition:
Integer Path (
floatingis falsy): Lodash useslower + Math.floor(Math.random() * (upper - lower + 1)). This ensures an inclusive, uniform distribution across all discrete integers in the range \([lower, upper]\).Floating-Point Path (
floatingis truthy): StandardMath.random()generates a pseudorandom number in the half-open interval \([0, 1)\). To make the upper bound inclusive for floats, Lodash cannot simply add1as it does with integers, as doing so would alter the scale of fractional values. Instead, it computes an epsilon offset based on string length and applies an upper clamp.
The Boundary Extension and Epsilon Logic
To make upper achievable in floating-point returns,
Lodash artificially extends the upper range by calculating an
infinitesimal offset based on the stringified precision of
Math.random():
var rand = Math.random();
var randLength = rand.toString().length - 1;
return Math.min(
lower + (rand * (upper - lower + parseFloat('1e-' + ((randLength <= 1 ? 16 : randLength) - 1)))),
upper
);Because Math.random() typically produces between 16 and
18 decimal places of precision, parseFloat('1e-16') (or
similar depending on platform precision) is added to the total range
(upper - lower).
Boundary Clamping and Distribution Distortion
The addition of the epsilon value slightly stretches the generation
window past the nominal upper limit. Consequently, any
random float generated that falls into the tiny interval
(upper, upper + epsilon] exceeds upper.
To prevent out-of-bounds results, Lodash wraps the calculation in
Math.min(..., upper). This clamping logic produces a
specific statistical artifact:
- Interior Uniformity: Across the range \([lower, upper)\), the distribution remains continuous and uniform.
- Upper Boundary Mass: Any value generated beyond
upperis mapped directly toupper. While the probability mass of this occurrence is infinitesimally small (roughly \(1 \times 10^{-16}\)), it technically introduces a discrete probability point at the exact upper boundary, creating a hybrid continuous-discrete distribution curve rather than a purely continuous one.