How Does GLSL Mod Work with Negative Numbers?

The built-in mod(x, y) function in the OpenGL Shading Language (GLSL) computes the remainder of division using floored division rather than truncated division. Unlike standard remainder operators in languages like C or C++, GLSL defines mod(x, y) as x - y * floor(x / y). This distinction causes negative input coordinates or values to wrap predictably into the positive range \([0, y)\) whenever the divisor \(y\) is positive, making it particularly useful for continuous texture mapping, cyclic procedural patterns, and grid generation.

The Mathematical Definition in GLSL

According to the official GLSL specification, the floating-point mod function computes:

\[\text{mod}(x, y) = x - y \cdot \text{floor}\left(\frac{x}{y}\right)\]

Because the equation relies on the floor function, division rounds down toward negative infinity rather than truncating toward zero. This mathematical approach is known as Knuth’s floored division.

Step-by-Step Calculation Examples

To see how this formula behaves with negative values, consider the following concrete evaluations:

Case 1: Negative Dividend, Positive Divisor (x = -0.25, y = 1.0)

  1. Compute the division: \(-0.25 / 1.0 = -0.25\)
  2. Apply the floor function: \(\text{floor}(-0.25) = -1.0\)
  3. Multiply by \(y\): \(1.0 \cdot (-1.0) = -1.0\)
  4. Subtract from \(x\): \(-0.25 - (-1.0) = 0.75\)

The result is \(0.75\), preserving a seamless repeating pattern across zero.

Case 2: Negative Integer-Like Float (x = -3.0, y = 2.0)

  1. Compute the division: \(-3.0 / 2.0 = -1.5\)
  2. Apply the floor function: \(\text{floor}(-1.5) = -2.0\)
  3. Multiply by \(y\): \(2.0 \cdot (-2.0) = -4.0\)
  4. Subtract from \(x\): \(-3.0 - (-4.0) = 1.0\)

Case 3: Positive Dividend, Negative Divisor (x = 3.0, y = -2.0)

  1. Compute the division: \(3.0 / -2.0 = -1.5\)
  2. Apply the floor function: \(\text{floor}(-1.5) = -2.0\)
  3. Multiply by \(y\): \(-2.0 \cdot (-2.0) = 4.0\)
  4. Subtract from \(x\): \(3.0 - 4.0 = -1.0\)

When \(y\) is negative, the result falls within the interval \((y, 0]\).

GLSL mod vs. C-Style Remainder %

In many CPU programming languages such as C, C++, and Java, the integer modulo operator % and the standard fmod() function perform truncated division, where the quotient is rounded toward zero:

\[\text{trunc\_mod}(x, y) = x - y \cdot \text{trunc}\left(\frac{x}{y}\right)\]

Under truncated division, -3.0 % 2.0 evaluates to -1.0. In contrast, GLSL's mod(-3.0, 2.0) evaluates to 1.0.

Expression C/C++ fmod(x, y) GLSL mod(x, y)
x = 1.25, y = 1.0 0.25 0.25
x = -0.25, y = 1.0 -0.25 0.75
x = -1.75, y = 1.0 -0.75 0.25
x = -3.0, y = 2.0 -1.0 1.0

Practical Implications in Shader Programming

The floored division behavior is essential for graphics programming because UV coordinates and world positions frequently cross the coordinate origin into negative space.