What Temperature Actually Does, Measured, and Why 0 Was Never Deterministic

Temperature, top-k, top-p and min-p from scratch, run on a real model. One setting picked a token the model rated under 1% four times in five.


Claude Opus 5.5’s migration guide has a line I didn’t expect: omit temperature, top_p and top_k, and use prompting to guide behaviour instead. It adds that if you were using temperature = 0 for determinism, it never guaranteed identical outputs on earlier models either.

So the most-tuned knob in LLM engineering is disappearing from at least one frontier API, and the main reason people set it to 0 was never true. That makes this a good moment to understand what these parameters actually did, so you know what you’re losing and what you weren’t getting anyway.

I implemented the four standard knobs from scratch in about 70 lines of numpy and ran them on a real model: SmolLM2-135M-Instruct, 20 prompts, 16 samples each, 64 tokens per sample.

The four knobs in one function

At every step a model produces a score (a logit) for every token in its vocabulary. Sampling turns those scores into one choice:

def filtered(logits, temperature=1.0, top_k=None, top_p=None, min_p=None):
    probs = softmax(logits / temperature)           # 1. reshape
    mask = np.ones(probs.size, dtype=bool)
    if top_k is not None:
        mask &= top_k_mask(probs, top_k)            # 2. keep the k likeliest
    if top_p is not None:
        mask &= top_p_mask(probs, top_p)            # 3. keep enough to cover p
    if min_p is not None:
        mask &= probs >= min_p * probs.max()        # 4. keep anything close to the best
    probs = np.where(mask, probs, 0.0)
    return probs / probs.sum()
  • Temperature reshapes the whole distribution. Dividing by 2 is the same as taking the square root of every probability and renormalizing, so unlikely tokens gain ground.
  • Top-k keeps a fixed number of candidates however confident the model is.
  • Top-p, or nucleus sampling from Holtzman et al., keeps the smallest set of top tokens whose probabilities add up to p.
  • Min-p, from Nguyen et al. (an ICLR 2025 oral), keeps every token that’s at least min_p times as likely as the best one.

Order matters. My code applies temperature first and then truncates, as Hugging Face transformers does. Not every server uses that order, so the same numbers can mean different things on a different stack.

What each setting actually samples

The measurement I care about is the tail-token rate: how often a setting picks a token that the model itself, before any temperature, rated under 1%. Those are the tokens where the output stops being what the model believed.

Bar chart of tail-token rate by setting: T=1.5 79.8%, T=1.5 with top-p 0.9 67.3%, T=1.0 11.4%, T=1.0 with top-k 40 6.4%, T=1.0 with top-p 0.9 4.4%, T=1.5 with min-p 0.1 2.4%, T=0.7 1.8%, T=1.0 with min-p 0.1 0.1%

SettingTail-token rateDistinct openingsRepeated trigrams
T=0.71.8%95.6%2.6%
T=1.011.4%99.1%0.8%
T=1.0, top-k 406.4%99.4%1.1%
T=1.0, top-p 0.94.4%97.8%0.9%
T=1.0, min-p 0.10.1%97.2%2.4%
T=1.579.8%100%0.0%
T=1.5, top-p 0.967.3%100%0.1%
T=1.5, min-p 0.12.4%99.1%0.8%

“Distinct openings” is the share of the 16 samples per prompt whose first 16 tokens were unique. “Repeated trigrams” measures loops within a sample.

Three things stand out.

Plain T=1.0 isn’t “the model’s natural distribution” in any useful sense. It picked a token the model rated under 1% about once every nine tokens. Over a 500-token answer, that’s dozens of small departures from what the model actually thought.

Top-p falls apart at high temperature. At T=1.5 the distribution is flat enough that the tokens needed to cover 90% of the probability still include a long tail, so top-p barely helps: 67% tail tokens instead of 80%. This is the failure the min-p paper set out to fix.

Min-p does what it claims, at least here. At T=1.5 it held the tail rate to 2.4%, close to T=0.7’s 1.8%, while keeping more distinct openings (99.1% against 95.6%). That’s the paper’s headline in miniature: you can raise the temperature for diversity without letting in the garbage.

This is a 135-million-parameter model, so treat the absolute numbers as illustrative. A frontier model’s distributions are sharper, and its tail tokens aren’t automatically wrong. The mechanics are the same, though, and that’s what changes how you set these knobs.

Temperature 0 and the determinism that wasn’t

Temperature 0 isn’t really a temperature, since you can’t divide by zero. Most implementations treat it as greedy decoding: take the top token, no randomness. So why would it ever vary?

I tested it locally first. Twenty prompts, 128 greedy tokens each, on CPU and on the Apple GPU:

ComparisonIdentical outputs
Same prompt, run twice20 / 20
Alone vs padded into a batch of 820 / 20
CPU vs GPU20 / 20

Perfectly repeatable. So I measured how close it came to not being. At each of 2,316 greedy steps I recorded the log-probability gap between the top two tokens, and the size of the numeric noise between devices:

MeasurementValue
Median gap between the top two tokens1.79
Smallest gap seen0.00041
Steps with a gap under 0.015 of 2,316
Largest CPU vs GPU difference in that gap0.000036
Difference between running alone and in a batch, on CPUexactly 0

The noise exists, but the closest call I saw was about 11 times bigger than the largest noise. A back-of-envelope extrapolation from those five near-ties, assuming they thin out evenly toward zero, puts a flip at roughly one per 100,000 tokens on this setup.

Now look at a hosted API. Thinking Machines sampled 1,000 completions at temperature 0 from Qwen3-235B and got 80 different outputs. All of them matched for the first 102 tokens. At token 103, 992 said “Queens, New York” and 8 said “New York City.”

Their explanation is the useful part. A hosted model is deterministic for any given batch, but the inference kernels give slightly different numbers at different batch sizes, and the batch size depends on how many other people are using the server at that moment. Your request’s arithmetic depends on strangers’ traffic. With much larger models, longer outputs, lower-precision arithmetic and millions of requests, the near-ties that were rare on my laptop happen all the time. Once one token flips, everything after it follows a different path.

They also showed it can be fixed with batch-invariant kernels, which gave 1,000 identical completions, at a real performance cost. The hosted APIs I checked don’t promise it: Anthropic’s guide says temperature 0 never guaranteed identical outputs, and OpenAI’s own cookbook says determinism with its seed parameter is not guaranteed.

What I’d do with this

If you need the same answer twice, store the first one. Don’t regenerate and hope. That goes for audit trails, caches and anything a human already approved.

If you need to know how variable an answer is, sample it k times on purpose. That’s what pass^k in evals is for.

Use temperature 0, where it’s still accepted, for extraction and classification, because you want the most likely answer. Don’t use it as a reproducibility guarantee.

On self-hosted models, try min-p before top-p when you want diversity. Servers such as vLLM and llama.cpp expose it. Check which order your server applies the filters in.

Where the knob is gone, ask for variety in the prompt. “Give three different options” does on purpose what a high temperature did by accident, and the model can make the options actually differ.

Run it

ajinb/llm-sampling-lab has the samplers with hand-computed tests (numpy only), and the experiment script that produced every number here. The experiment needs torch and transformers, downloads the 270 MB model on first run, and runs on a laptop.

Frequently asked questions

What does temperature do in an LLM?

Temperature divides the model's logits before they are turned into probabilities. Below 1 it sharpens the distribution toward the most likely token; above 1 it flattens it, so unlikely tokens are picked more often. At temperature 0, implementations skip sampling and take the most likely token, which is called greedy decoding.

What is the difference between top-p and min-p?

Top-p keeps the smallest set of most-likely tokens whose probabilities add up to p. Min-p keeps every token whose probability is at least min_p times that of the single most likely token. When the model is confident, both keep few tokens. When temperature has flattened the distribution, top-p keeps many low-probability tokens to reach its total, while min-p's cutoff still tracks the top token.

Is temperature 0 deterministic?

Not on a hosted API. A 2025 Thinking Machines study sampled 1,000 completions at temperature 0 from one model and got 80 different outputs, because inference kernels give slightly different results at different batch sizes and the batch size depends on server load. Anthropic's migration guide for Claude Opus 5.5 notes that temperature 0 never guaranteed identical outputs.

Comments