Contents · 6 sections↓
LLMs can write a working parser but miscount the letters in a word, reverse a short string wrong, or miss a word limit. That isn't carelessness. The model reads and writes tokens, chunks of text, not characters, and it produces each one by probability, not by counting. This essay explains tokenization in plain terms, shows real failures, and gives the rule that follows: send exact, character-level and counting work to code. It's for anyone who asks models for precise output.
Why can't LLMs count letters?
Because the model never sees letters. Text is split into tokens before the model reads it: "possessiveness" becomes three tokens, poss, ess and iveness. To count the s's, the model has to recall how each token is spelled, which it learned only indirectly, and then count, which it does by predicting digits that look right. Both steps are probabilistic, so the answer is often close and sometimes wrong.
Key takeaways:
Models read tokens, not characters. Common words are one token; rare words are a few pieces. Spelling is not directly visible.
Counting is prediction, too. A number in the output is the likeliest next token, not the result of a counter.
Reasoning helps, at a cost. With more reasoning effort, the model gets the letter count right in the example below, after spending extra tokens on it.
Exact work belongs in code. Counts, lengths, string reversal, dates and arithmetic are one line of Python and always right.
[02]
How tokenization works
Most current models use a variant of byte-pair encoding (BPE), introduced for neural translation by Sennrich, Haddow and Birch (2016). BPE starts from characters and repeatedly merges the most frequent adjacent pairs into new tokens, until the vocabulary reaches a fixed size. Frequent words end up as single tokens; rare words and numbers are split into pieces.
Here is OpenAI's o200k_base tokenizer (from the tiktoken library) on a few strings:
'possessiveness' -> ['poss', 'ess', 'iveness']
'unbreakableness' -> ['un', 'break', 'ab', 'leness']
' strawberry' -> [' strawberry']
'2026-10-18' -> ['202', '6', '-', '10', '-', '18']The model receives token IDs, numbers like 67395, not letters. Whatever it knows about how iveness is spelled, it learned from seeing it in text, not from looking at it. Dates and numbers get split at arbitrary places, which is one reason arithmetic on long numbers is unreliable without a tool.
Then comes sampling. The model outputs a probability for every possible next token and one is picked. For "how many s's?", the tokens 5 and 6 may both be likely. Nothing in the process checks the count.
[03]
Example: three small tasks
Run on gpt-5-mini with minimal reasoning effort; results vary by model and settings.
How many times does the letter 's' appear in the word 'possessiveness'?
Reply with the number only.The answer is 6. Asked several times, the model answered:
5, 5, 5, 6Reverse this string character by character. Output only the result: 'unbreakableness'The answer is ssenelbakaerbnu. Every answer was wrong, and one even contains an invisible character:
ssenelkabkaerbn u
ssendelkaerbnu
ssenelkabkearbnuAsking it to spell the word one letter per line first produced a correct spelling, and then a count of 7 every time: listing helps the model see the letters, but the counting step is still prediction. Word limits behave the same way. "Exactly 40 words" came back at 39 or 40 words: close, not exact.
With reasoning effort raised to low, the model answered 6 every time, using about a hundred hidden reasoning tokens per answer. That's the trade: more compute buys more accuracy on tasks where code would be free and exact.
[04]
What it changes in practice
Compute it in your code, not in the prompt. If you need a word count, a character count, a sorted list, a date difference or a sum, calculate it yourself and pass the result in, or check the model's output afterwards. len(text.split()) doesn't hallucinate.
Give the model a code tool for exact work. In an agent, a calculator or Python sandbox lets the model write the computation instead of guessing the answer. See Program-of-Thought and PAL.
Validate length and format limits in code. Treat "under 160 characters" as a request, then check it and retry or trim. Don't build a feature that depends on the model counting.
[05]
Frequently asked questions
Why can models write code but not count letters?
Code is made of common tokens in common patterns, which is exactly what prediction is good at. Counting letters needs character-level information the model doesn't see directly, plus an exact count it doesn't compute. Ask the model to write word.count("s") and it will get it right.
Is the "strawberry" problem fixed?
For that word, mostly, partly because it became famous and appears in training data with the right answer. The underlying cause, tokens instead of characters, is the same in every BPE-based model, so similar failures show up with less famous words.
What is a token, roughly?
A chunk of text from the model's vocabulary, often a common word, part of a word, a space plus a word, or a few digits. For English, a token averages a few characters. The exact split depends on the tokenizer, so different models see the same text differently.
Does a lower temperature fix counting errors?
It makes the output more repeatable, not more correct. If the most likely token is wrong, picking it every time gives you the same wrong answer every time. Many reasoning models don't expose temperature at all.
Should I ask the model to show its work?
It can help, but in the example above spelling the word out still led to a wrong count. For anything exact, code is the reliable path.
[06]
Next steps
Offload computation to code: PAL and Program-of-Thought prompting.
Run model-written code safely: Program-of-Thought in Python.
Plausible output that fails when run: AI code hallucinations.
How sampling shapes answers: LLM as a probability map.
Check what the model returns: output verification.