n4nAI

How chain-of-thought prompting improves math accuracy

Chain-of-thought prompting forces models to show their work, dramatically improving math accuracy by making reasoning explicit and verifiable.

n4n Team5 min read1,075 words

Audio narration

Coming soon — every post will get a voice note here.

Chain-of-thought prompting improves math accuracy by forcing the model to decompose problems into intermediate steps rather than jumping to answers. This mirrors how humans solve complex arithmetic: we don’t intuit 47 × 23, we break it into partial products. The technique shifts computation from opaque pattern matching to explicit reasoning, and the difference shows up clearly in evaluation benchmarks across model sizes.

Why standard prompting fails at math

Standard few-shot prompting treats math as a classification task. The model sees “What is 47 × 23?” and predicts the most likely token sequence following similar patterns in its training data. For simple arithmetic this works — multiplication tables are well-represented. But as soon as problems require multi-step reasoning, the probability of the correct final token plummets because the model never learned to “carry the one” as a distinct operation.

Consider this zero-shot prompt:

prompt = "What is 47 * 23?"

A 7B parameter model typically answers 1081 (correct) or 1082 (off by one). But ask it to solve (47 * 23) + (15 * 32) - 100 and accuracy collapses. The model tries to predict the final number directly, hallucinating intermediate values that never get verified.

The failure mode is structural: next-token prediction has no built-in mechanism for self-correction. Once the first digit of the answer is wrong, every subsequent token conditions on that error.

How chain-of-thought changes the computation

Chain-of-thought prompting appends “Let’s think step by step” or provides explicit reasoning examples. This does two things mechanically:

  1. Expands the context window with intermediate tokens that serve as a scratchpad
  2. Conditions each step on the previous step’s output, creating a verification chain
cot_prompt = """Q: What is (47 * 23) + (15 * 32) - 100?
A: Let's think step by step.
First, calculate 47 * 23:
47 * 20 = 940
47 * 3 = 141
940 + 141 = 1081

Next, calculate 15 * 32:
15 * 30 = 450
15 * 2 = 30
450 + 30 = 480

Now add them: 1081 + 480 = 1561

Finally subtract 100: 1561 - 100 = 1461

The answer is 1461."""

The model now generates 1461 after producing verifiable intermediate values. Each arithmetic operation becomes a separate prediction conditioned on correct predecessors. Errors still occur, but they’re localized — a mistake in 47 × 23 doesn’t cascade silently into the final answer because the addition step conditions on the written intermediate result.

Concrete comparison on GSM8K-style problems

The GSM8K dataset (grade-school math word problems) is the standard benchmark for chain-of-thought math accuracy. Here’s what the difference looks like in practice:

import json

problems = [
    "Janet has 24 apples. She gives 1/3 to her brother and 1/4 to her sister. How many apples does she have left?",
    "A train travels 60 mph for 2.5 hours, then 70 mph for 1.5 hours. What is the total distance?",
    "If 3 shirts cost $45, how much do 7 shirts cost?"
]

standard_prompt = "Q: {}\nA:"
cot_prompt = "Q: {}\nA: Let's think step by step.\n"

# Standard prompting typically yields:
# "A: 12" (wrong - no reasoning shown)
# "A: 225 miles" (correct but unverifiable)
# "A: $105" (correct but brittle)

# Chain-of-thought yields:
# "A: Let's think step by step.
# Janet starts with 24 apples.
# 1/3 to brother: 24 * 1/3 = 8 apples.
# 1/4 to sister: 24 * 1/4 = 6 apples.
# Total given away: 8 + 6 = 14 apples.
# Remaining: 24 - 14 = 10 apples.
# The answer is 10."

On a 7B model, standard prompting achieves roughly 15-25% accuracy on GSM8K. Chain-of-thought pushes this to 45-55%. On 70B+ models, the gap narrows but persists: 70% vs 85%. The improvement comes from making the model do the work in the forward pass rather than compressing reasoning into a single token prediction.

The mechanism: why intermediate tokens help

Transformers compute attention over all previous tokens. When you force the model to write “47 * 20 = 940”, that equation becomes part of the context for predicting “940 + 141 = 1081”. The attention mechanism can now attend to the explicit multiplication rather than reconstructing it from weights.

This is computationally distinct from internal reasoning. The model’s weights could encode multiplication algorithms, but gradient descent optimizes for next-token prediction on internet text — which rarely shows step-by-step arithmetic. Chain-of-thought moves the algorithm from weights (where it’s poorly learned) to activations (where it’s explicitly executed).

# What the model effectively does with CoT:
# Step 1: Attend to "47 * 20" → predict "940"
# Step 2: Attend to "47 * 3" → predict "141"  
# Step 3: Attend to "940 + 141" → predict "1081"
# Each step is a simple pattern the model saw during training

# Without CoT:
# Step 1: Attend to "47 * 23" → predict "1081" (memorized) or hallucinate

The model isn’t “thinking” — it’s executing a program written in natural language, where each line is a simple next-token prediction it handles reliably.

Tradeoffs you need to weigh

Latency and cost

Chain-of-thought increases output tokens 3-10x. On a typical GSM8K problem, standard prompting generates ~5 tokens. CoT generates 50-200. At $0.50-2.00 per million output tokens (depending on provider), this adds up fast at scale.

# Rough token economics for 1M math problems:
# Standard: ~5M output tokens → $2.50-10
# CoT: ~100M output tokens → $50-200

If you’re running a high-volume API, this matters. Some teams use a two-stage approach: cheap model generates CoT, expensive model verifies the final answer only.

Verbosity control

Unconstrained CoT produces inconsistent formatting. Some models write paragraphs, others bullet points. This breaks downstream parsing.

# Better: constrain the format
cot_prompt_structured = """Q: {}
A: Let's solve this step by step.
Step 1: [operation]
Result: [value]
Step 2: [operation]
Result: [value]
Final answer: [number]"""

Structured CoT adds ~10% more tokens but makes extraction reliable. Use regex or a small parser rather than another LLM call.

When CoT hurts

Chain-of-thought can reduce accuracy on:

  • Simple retrieval: “What is 7 * 8?” — CoT adds noise
  • Pattern matching: “Complete the sequence: 2, 4, 8, 16, ?” — explicit reasoning may override correct intuition
  • Adversarial prompts: CoT gives attackers more surface area for injection

Test your specific problem distribution. Don’t assume CoT helps universally.

Alternatives and complements

Program-aided language (PAL)

Instead of natural language reasoning, have the model write Python:

pal_prompt = """Q: Janet has 24 apples. She gives 1/3 to her brother and 1/4 to her sister. How many apples does she have left?
A: Let's write a Python program to solve this.

total = 24
brother = total * 1/3
sister = total * 1/4
remaining = total - brother - sister
print(remaining)"""

PAL offloads arithmetic to a Python interpreter — zero hallucination on calculation steps. The model only needs to translate the problem correctly. Accuracy on GSM8K jumps another 10-15% over CoT. The tradeoff: you need a code execution environment, and the model must generate valid syntax.

Self-consistency

Run CoT multiple times (temperature > 0) and take the majority answer:

import statistics

def self_consistency(prompt, n=5, temperature=0.7):
    answers = []
    for _ in range(n):
        response = model.generate(prompt, temperature=temperature)
        answer = extract_final_number(response)
        answers.append(answer)
    return statistics.mode(answers)

This costs 5x inference but can push 7B models from 55% to 70%+ on GSM8K. Worth it when accuracy is critical and latency isn’t.

Fine-tuning on CoT traces

If you control the model, fine-tune on high-quality CoT examples. This bakes the reasoning pattern into weights, eliminating the token overhead at inference. The resulting model solves math problems in fewer tokens because it “internalized” the steps. This is how models like DeepSeek-Math and WizardMath achieve strong results without explicit CoT prompting.

Practical implementation pattern

Here’s a production-ready pattern for math-heavy workloads:

import re
from typing import Optional

class MathSolver:
    def __init__(self, client, model: str):
        self.client = client
        self.model = model
    
    def solve(self, problem: str, use_cot: bool = True) -> dict:
        if use_cot:
            prompt = self._cot_prompt(problem)
            response = self.client.chat.completions.create(
                model=self.model,
                messages=[{"role": "user", "content": prompt}],
                temperature=0.0,  # Deterministic for math
                max_tokens=500
            )
            reasoning = response.choices[0].message.content
            answer = self._extract_answer(reasoning)
            return {"answer": answer, "reasoning": reasoning, "method": "cot"}
        else:
            prompt = f"Q: {problem}\nA:"
            response = self.client.chat.completions.create(
                model=self.model,
                messages=[{"role": "user", "content": prompt}],
                temperature=0.0,
                max_tokens=20
            )
            answer = self._extract_answer(response.choices[0].message.content)
            return {"answer": answer, "reasoning": None, "method": "direct"}
    
    def _cot_prompt(self, problem: str) -> str:
        return f"""Q: {problem}
A: Let's solve this step by step.
Step 1: Identify what we know and what we need to find.
Step 2: Perform each calculation explicitly.
Step 3: State the final answer clearly.
"""
    
    def _extract_answer(self, text: str) -> Optional[float]:
        # Match "The answer is X" or "Final answer: X" or just trailing number
        patterns = [
            r"(?:answer|result)\s*(?:is|=|:)\s*([\d,]+\.?\d*)",
            r"([\d,]+\.?\d*)\s*$"
        ]
        for pattern in patterns:
            match = re.search(pattern, text, re.IGNORECASE)
            if match:
                return float(match.group(1).replace(",", ""))
        return None

Key decisions in this pattern:

  • Temperature 0 — math should be deterministic
  • Explicit extraction — don’t rely on the model formatting perfectly
  • Method tracking — log which approach you used for evaluation
  • Max tokens capped — prevents runaway CoT on pathological inputs

Evaluation: measure what matters

Don’t trust benchmark numbers. Evaluate on your problem distribution:

def evaluate_solver(solver, test_cases: list[dict], use_cot: bool):
    correct = 0
    total = 0
    for case in test_cases:
        result = solver.solve(case["problem"], use_cot=use_cot)
        if abs(result["answer"] - case["answer"]) < 0.001:
            correct += 1
        total += 1
    return correct / total

# Your test cases should reflect real usage:
test_cases = [
    {"problem": "Calculate 15% tip on $87.50", "answer": 13.125},
    {"problem": "If 5 servers handle 200 req/s, how many for 500 req/s?", "answer": 12.5},
    {"problem": "Compound interest: $1000 at 5% for 3 years", "answer": 1157.625},
]

Track accuracy and latency and token cost. A 5% accuracy gain that doubles p99 latency may not be worth it for your API.

The decisive takeaway

Chain-of-thought prompting improves math accuracy because it converts implicit reasoning into explicit tokens that the transformer can attend to and verify. It’s not magic — it’s moving computation from poorly-learned weights into the forward pass where each step is a simple, high-probability prediction.

Use it when:

  • Problems require 3+ reasoning steps
  • You can tolerate 3-10x output tokens
  • You implement structured parsing and deterministic sampling

Skip it when:

  • Problems are single-step arithmetic or retrieval
  • Latency budget is tight and accuracy is already acceptable
  • You can use PAL with a code executor instead (better accuracy, similar cost)

The engineers who get the most mileage from CoT treat it as a programming technique — they write the reasoning program in the prompt, constrain the output format, and measure rigorously on their actual workload. The rest treat it as a magic phrase and wonder why their p99 latency spiked.

Tagschain-of-thoughtmath-reasoningprompt-engineering

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All chain-of-thought prompting posts →