luaguides

math.min

math.min(x [, ...])

math.min returns the smallest of its numeric arguments in Lua 5.4. Call it with one or more numbers and you get back the original argument that compares as less than or equal to every other one under Lua’s < operator.

Syntax

math.min(x [, x2, ...])

The function is variadic. Pass a single number and it returns that number unchanged. Pass several and it picks the smallest.

Parameters

#NameTypeRequiredDefaultDescription
1xnumberyesFirst value, used as the running minimum.
2+...numbernoAdditional values compared against the current minimum.

Return value

math.min returns one of its arguments unchanged. The returned value keeps the integer or float subtype of whichever argument was selected. So math.min(1, 2.5) returns the integer 1, while math.min(1.5, 2) returns the float 1.5. The function never coerces, casts, or rounds.

Examples

Basic usage

print(math.min(2, 4, 6, 8))       --> 2
print(math.min(-3, -1, -7))       --> -7
print(math.min(0.1, 0.01, 0.001)) --> 0.001
print(math.min(5))                --> 5

These examples cover the standard cases: many arguments, a single argument, negative numbers, and small fractional values. The single-argument form is an identity: math.min(x) returns x because the comparison loop never runs when there is nothing else to compare against. Notice that the value returned is always one of the original arguments, never a transformed copy. The comparison itself is the Lua < operator, so any value that responds to < participates correctly.

Mixed integer and float arguments

print(math.min(1, 2.5, 3))    --> 1
print(math.min(1.5, 2, 3))    --> 1.5
print(type(math.min(1, 2)))   --> integer
print(type(math.min(1.0, 2))) --> float

Lua 5.4 keeps integers and floats as separate subtypes, and math.min preserves whichever subtype wins the comparison. When all arguments are integers and an integer is the smallest, the return is an integer. When a float happens to be the smallest, the return is a float, even if its value is integral (the 1.0 case above shows this). This matters any time downstream code cares about the difference, for example when formatting output, dispatching on a tag, or serializing to a protocol that distinguishes the two subtypes.

Infinity and NaN

math.huge and -math.huge are valid numeric arguments and behave like any other float. Both are IEEE 754 infinities under the hood: math.huge is HUGE_VAL from C, the platform-dependent positive infinity, and -math.huge is its negation. Mixing them with ordinary numbers follows standard infinity arithmetic, so every finite number compares as less than math.huge and greater than -math.huge, which makes both constants safe to use as sentinels in a reduction.

print(math.min(math.huge, 0))           --> 0
print(math.min(-math.huge, 0))          --> -inf
print(math.min(math.huge, -math.huge))  --> -inf

NaN behaves differently because NaN < x and x < NaN are both false in Lua. A NaN never displaces an existing minimum, so if NaN comes first it stays put. The two examples below show each side: NaN in the second slot does not displace an earlier minimum, while NaN in the first slot is sticky and gets returned. Watch out for this when your data could include results of failed divisions or 0/0.

print(math.min(5, 0/0))   --> 5
print(math.min(0/0, 5))   --> -nan

Zero arguments

Calling math.min() with no arguments raises an error. The check fires before the comparison loop runs, so no other valid values can rescue the call. If you need a neutral starting value for an accumulator, for example when folding a list whose range you do not yet know, seed it with math.huge. The reason math.huge works as an identity is that every finite number compares as less than it, so the accumulator can only ever decrease from there.

print(math.min())
-- stdin:1: bad argument #1 to 'min' (value expected)

Minimum of a numeric table

A small loop is faster than collecting the values and calling math.min(table.unpack(t, 1, n)), especially for large tables. The reason is that table.unpack has to push the argument list onto the stack before math.min can read it, and the function then runs its own comparison loop on top. A direct fold keeps both the overhead and the allocation minimal, and the same pattern generalizes to any associative reduction: sums, products, math.max, and string concatenations all benefit from a hand-rolled accumulator with an identity element.

local function table_min(t)
  local m = math.huge
  for i = 1, #t do
    if t[i] < m then m = t[i] end
  end
  return m
end

print(table_min({3, 1, 4, 1, 5, 9, 2, 6}))  --> 1

Custom objects with __lt

Because the comparison inside math.min is just the Lua < operator, two values of the same non-numeric type that share an __lt metamethod are compared through that metamethod. The math library does not add a comparison of its own, it just delegates to the language operator. Mixed-type comparisons (a table versus a number) still raise an error, because Lua only consults metamethods when both operands share the same non-numeric type.

local Vec = {__lt = function(a, b) return a.x < b.x end}
local a = setmetatable({x = 1, y = 0}, Vec)
local b = setmetatable({x = 2, y = 0}, Vec)
print(math.min(a, b).x)  --> 1

Common pitfalls

Zero arguments. math.min() errors. Use math.huge as the identity when folding over a collection.

NaN at index 1 sticks. Because the comparison loop uses <, a leading NaN is never replaced. Place known-good values first if your data could contain NaN.

No string coercion. math.min(1, "2") raises attempt to compare number with string. Convert explicitly with tonumber first.

Non-numeric arguments need a matching metamethod. Two tables sharing a metatable with an __lt metamethod work, but a table and a number do not, because Lua only consults metamethods when both operands are the same non-numeric type.

See also