Skip to content
AI360Xpert
Beta

Function Notation, Domain and Range

The notation f(x) names outputs, the domain lists allowed inputs, and the range lists possible outputs. Shape errors, NaNs and silent clippings are all domain violations.

Inputs 0 and 3 map through 1 over x minus 2 to minus 0.5 and 1, while x equals 2 has no arrow at all because the domain forbids it
Inputs 0 and 3 map through 1 over x minus 2 to minus 0.5 and 1, while x equals 2 has no arrow at all because the domain forbids it

Why Does This Exist?

Every function has fine print: which inputs it accepts and which outputs it can produce. Feed a square root a negative, divide by zero, or log a zero, and the math has no answer, so the code invents one, usually NaN. Half of all debugging sessions are domain violations wearing costumes: the embedding lookup past the vocabulary edge, the probability 1.0 inside a log, the division by an empty count.

Range matters just as much. A sigmoid can never output exactly 0 or 1, so a loss waiting for those values waits forever. Stating domain and range up front turns runtime surprises into design facts. Distributions with their supports build on this in probability distributions; the mapping idea itself is in functions and graphs.

The one idea you need first. The notation f(x)f(x) reads "f of x": the output of function ff at input xx. The domain is the allowed-input list, the range the possible-output list. Three words, and they prevent most invalid-input bugs.

Think of It Like This

A parking garage with a height bar

A garage hangs a bar at 2 metres: taller vans cannot enter, full stop. Inside, every bay is on floors 1 to 5: no bay exists elsewhere. The bar is the domain, the floors are the range.

Code guards are height bars: if x <= 0: reject before the log, vocabulary size before the embedding lookup. And just as a driver who ignores the bar loses a roof rack, a pipeline that ignores the domain loses a night to NaNs.

Where it stops: garages turn away vans politely, while math has no waiting room. An input outside the domain does not queue; the function simply has no value there.

How It Actually Works

Read the formula for forbidden operations. Division forbids zero denominators: f(x)=1/xf(x) = 1/x has domain x≠0x \neq 0. Even roots forbid negatives: x\sqrt{x} needs x≥0x \ge 0. Logs forbid non-positives: ln⁡(x)\ln(x) needs x>0x > 0. Polynomials and exponentials accept everything: domain all reals.

Find the range by asking what outputs are reachable. x2x^2 never goes negative: range y≥0y \ge 0. Sigmoid squeezes to (0,1)(0, 1), endpoints excluded. Interval notation writes it compactly: [0,∞)[0, \infty) includes 0, (0,1)(0, 1) excludes both ends.

Worked example: one over x minus 2

Function: f(x)=1/(x−2)f(x) = 1/(x - 2). The denominator vanishes at x=2x = 2, so the domain is all reals except 2.

Evaluate around the hole. f(0)=1/(0−2)=−0.5f(0) = 1/(0 - 2) = -0.5. f(1)=1/(1−2)=−1f(1) = 1/(1 - 2) = -1. f(3)=1/(3−2)=1f(3) = 1/(3 - 2) = 1. f(4)=1/(4−2)=0.5f(4) = 1/(4 - 2) = 0.5. The outputs skip nothing except 00 itself: no input gives 1/(x−2)=01/(x-2) = 0, since a fraction is zero only with a zero numerator. Range: all nonzero reals. The diagram above maps two of these arrows and leaves x=2x = 2 with none.

Code

def f(x: float) -> float:    # 1/(x - 2); the domain bar hangs here, not inside the division.    if x == 2.0:        return float("nan")    return 1.0 / (x - 2.0)

print(f(0.0))  # -> -0.5print(f(3.0))  # -> 1.0print(f(2.0))  # -> nan

The guard names the domain before computing, turning a crash (ZeroDivisionError) into an explicit, searchable NaN at the exact forbidden input. Production code logs or rejects here; the shape of the guard is what matters.

Watch Out For

Square roots of negatives deep in a pipeline

Standardising features with a variance term that rounds slightly negative hands sqrt a value like −10−16-10^{-16}. NumPy returns NaN with a warning nobody reads, and the NaN rides every downstream computation to the loss. The symptom is a fully-NaN epoch with healthy-looking inputs.

Clip before rooting: math.sqrt(max(v, 0.0)). Assert non-negativity where the value is produced, not where it is consumed.

Waiting for outputs the range cannot produce

Cross-entropy with a sigmoid output can approach 0 loss but never reach it, because the sigmoid never outputs exactly 0 or 1. A convergence check demanding loss exactly 0.00.0 waits forever, and a test asserting exact probabilities fails on correct code.

Set thresholds inside the range: stop below 10−410^{-4}, not at zero. Read the range off the activation before writing the assertion.

The Quick Version

  • f(x)f(x) reads "f of x"; domain lists allowed inputs, range lists reachable outputs.
  • Division, even roots and logs restrict domains; polynomials take everything.
  • 1/(x−2)1/(x - 2) excludes x=2x = 2 and never outputs 00; test around the hole.
  • Guard domains in code before computing, with explicit NaNs or rejections.
  • Never assert outputs outside the range, like exact 0 from a sigmoid.