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 % 1

If 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:

  1. Integer Path (floating is falsy): Lodash uses lower + Math.floor(Math.random() * (upper - lower + 1)). This ensures an inclusive, uniform distribution across all discrete integers in the range \([lower, upper]\).

  2. Floating-Point Path (floating is truthy): Standard Math.random() generates a pseudorandom number in the half-open interval \([0, 1)\). To make the upper bound inclusive for floats, Lodash cannot simply add 1 as 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: