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:

  1. Default Bounds: If no arguments are passed, it defaults to lower = 0 and upper = 1. If only one value is provided, it assigns that value to upper and sets lower to 0.
  2. Inverted Ranges: If lower is greater than upper, the method swaps their values to maintain a valid interval.
  3. Floating-Point Inference: Lodash checks whether a floating-point result is required. A result is treated as floating-point if:
    • The floating boolean parameter is explicitly set to true.
    • Either lower or upper is already a floating-point number (detected using modulus arithmetic, such as lower % 1 !== 0 or upper % 1 !== 0).

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.