Skip to content

Can AI do cups to grams? We tested 5 models

By Chris Artmann ·

Say you're building a recipe app. A user pastes in an American recipe and wants grams. You already have an LLM in the app. Why not just ask it?

It's a fair question. Converting cups to grams isn't simple arithmetic. A cup of flour weighs about 120 grams. A cup of sugar weighs about 198. You need a density for every ingredient, and we wrote a whole post about that: cups to grams needs a density table. But language models have read a lot of baking blogs. Maybe they've learned the table.

So we tested it. We gave five models the same 31 conversions and scored every answer.

The short version: the big models are good. Surprisingly good. The small ones are not even close. And the interesting part is where the big ones disagree with the reference, because sometimes the reference is the one that's off.

How we tested

We picked 30 conversions that a real recipe app would see. Plain flour and sugar. Packed brown sugar. Butter by the cup and by the stick. Honey, which is heavy. Oats, which are light. Three kinds of salt. Two lines from an Australian recipe, where a cup is 250 ml. Two from a Swedish recipe, which uses decilitres. We added one more line with no right answer, "1 heaping tablespoon flour", just to see what happened.

The reference answers come from King Arthur Baking's ingredient weight chart. It's the same table we used in the cups-to-grams post. For the metric cups and decilitres, we scaled King Arthur's per-cup numbers by volume. An answer counts as correct if it's within 10% of the reference. That's about the error you get from scooping flour a bit differently, so it's a fair bar.

Here's the exact prompt. Every model got it word for word:

Convert each of these recipe amounts to grams. Assume a US customary
cup (236.6 ml) unless the line says otherwise. Give one number per
line, not a range.

1. 1 cup all-purpose flour
2. 1 3/4 cups all-purpose flour
3. 1 cup bread flour
...
31. 1 heaping tablespoon all-purpose flour

Answer with JSON only, no other text: an object mapping each number
to grams, like {"1": 100, "2": 50}.

The models were Claude Haiku 5.5, Sonnet 5.5 and Opus 5.5, plus two small open models running locally: Qwen 2.5 3B and Llama 3.2 3B. We wanted GPT and Gemini in there too, but we didn't have keys set up on the day. Treat this as a test of "big hosted model vs small local model", not a ranking of AI companies.

A few honest notes on method. We sent all the conversions in one prompt, not one call each. That's cheaper, and it's how you'd probably do it in an app, but it's not the same as asking one at a time. We ran each model three times. The Claude runs went through the Claude Code CLI with tools turned off, and that doesn't let you set temperature, so you'll see some run-to-run variation. The local models ran at temperature 0.

Here's how they did:

Model Correct (within 10%) Same answer in all 3 runs
Claude Haiku 5.5 84 of 90 (93%) 27 of 31 lines
Claude Sonnet 5.5 83 of 90 (92%) 26 of 31 lines
Claude Opus 5.5 79 of 90 (88%) 18 of 31 lines
Qwen 2.5 3B 22 of 90 (24%) 21 of 31 lines
Llama 3.2 3B Couldn't follow the format n/a

Yes, the smallest Claude model beat the biggest one. Hold that thought.

What went wrong, and why

Let's start with the small models, because their mistakes are the fun ones.

Qwen 2.5 3B said a cup of honey weighs 45 grams. Honey is one of the heaviest things in your kitchen. A cup is about 336 grams. It said a cup of butter is 81 grams, and so is a stick of butter, and so is half a cup of peanut butter. Three different lines, one number. It put a cup of rolled oats at 250 grams, nearly three times too much. And it said a tablespoon of Diamond Crystal kosher salt weighs 89 grams, which is more than a whole cup of oats. It also scaled the wrong way a few times. One and three-quarter cups of flour came out at 397 grams, but one cup came out at 146.

Llama 3.2 3B didn't get that far. Instead of numbering its answers 1 to 31, it used the fractions from the questions as keys. So you get "1": 120, "3/4": 125, "1": 1200, "1": 400 and so on. JSON can't hold the same key twice, so most of the answers overwrite each other. If you read them in order anyway, one says a cup of bread flour weighs 1,200 grams. We tried again without forcing JSON mode and got the exact same thing back.

That's lesson one. A small model isn't just less accurate, it's unpredictable in ways your code has to handle. If your app parses its output, a broken format is a bug, not a wrong answer.

The big models are a different story. Their misses are small and mostly explainable. And some of them aren't really misses.

Look at vegetable oil. All three Claude models said a cup weighs 218 grams, every single run. King Arthur says 198. So the models "failed". But oil has a density of about 0.92 grams per millilitre, and 0.92 times 236.6 ml is 218. The models did the physics. King Arthur gives a kitchen number. The same thing happened with milk (models said 244, King Arthur says 227) and water (237 vs 227). Those landed inside the 10% bar, but it's the same pattern. As we said in the cups-to-grams post, King Arthur rounds to friendly numbers made for bakers. Neither answer is wrong. They just come from different tables.

Opus had the most interesting "miss" of all. We asked for 2 dl of flour from a Swedish recipe. Scaling King Arthur's cup gives 101 grams. Opus said 120, all three times. That's not random. Swedish kitchen tables usually say 1 dl of wheat flour weighs 60 grams, so 2 dl is 120. Opus used the Swedish answer for the Swedish recipe. You could argue that's the most correct answer in the whole test.

But Opus also wobbled. It gave caster sugar as 200, 220 and 190 grams in three runs. It put an Australian metric cup of flour at 150 grams once and 132 grams twice. It only gave the same answer all three times on 18 of 31 lines. Haiku and Sonnet were steadier. A bigger model knows more, but for a lookup like this, "knows more" can also mean "has more opinions".

The salt lines show the same thing. Everyone got table salt right at 18 grams per tablespoon. Diamond Crystal kosher salt, which is famously light, came out at 8 or 9 grams. That's impressive. Morton kosher salt came out at 14 or 15 against King Arthur's 16, which is right on the edge. Salt brands publish their own numbers, and they don't all agree.

And the heaping tablespoon? There's no right answer, so we didn't score it. Haiku said 10 to 12 grams. Sonnet said 12. Opus said 15. Qwen said 8. That's a spread of nearly double, for a word that shows up in thousands of recipes. We dig into words like that in teaching an AI to understand vague recipe language.

So why do the errors happen? There are three main reasons. First, density changes from one ingredient to the next, and the model has to remember a number for each one. It's recalling, not computing. Second, cups aren't one size. US, US nutrition label, metric and Swedish decilitres all differ, and the model has to notice which one the recipe means. Third, how something goes in the cup matters. Packed or loose, sifted or scooped, Morton or Diamond Crystal. A model that knows all of this still has to pick one answer, and different sources picked different ones.

Here are all the answers, so you can check our scoring. Where a model gave different answers across runs, they're separated by slashes.

# Line Reference Haiku Sonnet Opus Qwen 3B
1 1 cup all-purpose flour 120 125 120 120/125 146
2 1¾ cups all-purpose flour 210 219 210 210/219 397
3 1 cup bread flour 120 127 127 120/130 148/128
4 1 cup whole wheat flour 113 120 113 113/120 142/149
5 1 cup granulated sugar 198 200 200 200 204
6 ⅓ cup granulated sugar 66 67 67 67 34
7 1 cup brown sugar, packed 213 213/220 213 213/220 220
8 1 cup confectioners' sugar 113 120 113 113/120 120/140
9 1 cup caster sugar 190 200 200 200/220/190 200/180
10 ½ cup cocoa 42 43 42 42/43 42/30
11 ¼ cup cornstarch 28 30 30 30/32 17
12 1 cup rolled oats 89 90 90/89 90 250
13 1 cup long grain rice, dry 198 185 185 185 260
14 1 cup butter 226 227 227 227 81
15 1 stick of butter 113 113 113 113 81
16 ½ cup peanut butter 135 128/129 135/128 135 81
17 1 cup honey 336 340 340 340 45
18 1 tbsp honey 21 21 21 21 4.5
19 1 cup vegetable oil 198 218 218 218 250
20 ¼ cup olive oil 50 54 54 54 30/14
21 1 cup milk 227 244/245 245/244 244/245 237
22 1 cup water 227 237 237 237 237
23 1 tbsp table salt 18 18 18 18 6/5
24 1 tbsp Morton kosher salt 16 14 14/15 15 5
25 1 tbsp Diamond Crystal kosher salt 8 8 8/9 9/8 89/68
26 1 metric cup (250 ml) flour 127 132 127 150/132 146/143
27 1 metric cup (250 ml) sugar 209 211 211 220/211 204
28 3 dl sugar 251 254 254 255 125/192
29 2 dl flour (vetemjöl) 101 106 101 120 50
30 1 level tbsp flour 7.5 8 8 8 5
31 1 heaping tbsp flour none 12/10 12 15 8

So, should you let an LLM convert cups to grams in your app? If it's a big model and the stakes are low, it'll usually be close. But "usually close" has some catches. The same model can give you a different number tomorrow. You can't say which table it used. And if you ever swap in a smaller, cheaper model to save money, everything falls apart.

The better pattern is to let the model do what it's good at, which is reading the messy recipe line, and hand the maths to code. Give it a tool. The model figures out that "1¾ cups flour, spooned and leveled" means 1.75, cup and all-purpose flour. Your function looks up the density and does the arithmetic. Same answer every time, from a table you chose.

Here's what that looks like with the Anthropic SDK. The toGrams function is the small converter from the cups-to-grams post:

import Anthropic from '@anthropic-ai/sdk'
import { toGrams } from './to-grams'

const client = new Anthropic()

const tools: Anthropic.Tool[] = [
  {
    name: 'convert_to_grams',
    description:
      'Convert a volume of one ingredient to grams using a fixed density table. ' +
      'Always use this instead of estimating a weight yourself. ' +
      'Returns null when the ingredient is unknown; then keep the original volume.',
    input_schema: {
      type: 'object',
      properties: {
        amount: { type: 'number', description: 'For example 1.75' },
        unit: { type: 'string', enum: ['cup', 'tbsp', 'tsp', 'ml', 'dl', 'l'] },
        ingredient: { type: 'string', description: 'For example "all-purpose flour"' }
      },
      required: ['amount', 'unit', 'ingredient']
    }
  }
]

const response = await client.messages.create({
  model: 'claude-haiku-5-5',
  max_tokens: 1024,
  tools,
  messages: [
    { role: 'user', content: 'Convert to grams: 1¾ cups flour, spooned and leveled' }
  ]
})

for (const block of response.content) {
  if (block.type === 'tool_use') {
    const { amount, unit, ingredient } = block.input as {
      amount: number
      unit: 'cup' | 'tbsp' | 'tsp' | 'ml' | 'dl' | 'l'
      ingredient: string
    }
    console.log(toGrams(amount, unit, ingredient) ?? 'unknown, keep the volume')
  }
}

Notice the description says "always use this instead of estimating". Without it, a big model is quite happy to answer from memory, and it'll often be right, which is exactly what makes it easy to miss when it isn't. And notice the null case. A tool that says "I don't know this one" is much better than a model that confidently guesses. If you're new to tools like this, our plain-English guide to MCP explains how the same idea works across chat apps.

Pulling the ingredient name and amount out of the line is the hard part, and we wrote that up in how to parse "2 cups flour, sifted". If you also need to scale recipes, read why you can't just double a recipe first, because conversion and scaling interact in fun ways.

Or skip the table entirely. The Tiny Plates API converts a whole recipe for you when you add units=metric. It uses a density table keyed by ingredient. It leaves an ingredient in cups when we don't have a trusted density for it, and it tells you which ones. Over MCP, the get_recipe tool takes the same option, so an agent gets grams from the API instead of from its memory. Either way, the number comes from a table, not from a guess.