A user in London opens your tank volume calculator, enters the dimensions of a standard cylindrical tank, and gets a result that is 20 percent too high. A user in Texas enters the same dimensions into the same calculator and gets the correct answer. Same code, same inputs, wildly different correctness. The bug is not in your math. It is in your gallon.
There are two different gallons in common use, they differ by about 20 percent, and most code treats them as if they were the same thing. This post walks through where the two gallons come from, how the bug shows up in real code, how much damage it does in litres, and how to design your conversion layer so it never bites you.
Two gallons, one name
| Unit | Litres | Cubic inches |
|---|---|---|
| US liquid gallon | 3.785411784 | 231 (exact) |
| Imperial gallon | 4.54609 | about 277.4 |
The imperial gallon is 20.09 percent larger than the US gallon. That is not a rounding difference you can ignore. It is the difference between a correct result and a wrong one on every single conversion.
A short history explains the split. Britain standardized the imperial gallon in 1824, defining it by the volume of 10 pounds of water. The United States kept the older Queen Anne wine gallon of 231 cubic inches, which it had inherited from colonial practice. Neither side was wrong; they simply froze different definitions in law. Your code, unfortunately, has to pick one, and it has to pick knowingly.
How the bug looks in code
Here is the pattern that causes the trouble. It appears in calculators, IoT dashboards, aquarium tools, fuel trackers, and brewing apps:
function litresToGallons(litres) {
return litres / 3.785; // which gallon is this?
}
The constant 3.785 is the US gallon, but nothing in the function says so. A developer in Manchester copies the snippet, the math checks out in testing (the function does convert litres to a gallon), and the bug ships. Every imperial user gets results inflated by 20 percent, forever, until someone files a very confused bug report.
The reverse direction is just as common:
function gallonsToLitres(gallons) {
return gallons * 4.546; // imperial constant, US audience
}
The damage, in real numbers
Take a cylindrical tank with a radius of 0.5 m and a height of 1.2 m. Its volume is:
V = π × 0.5² × 1.2 = 0.9425 m³ = 942.5 litres
Now convert that volume to gallons both ways:
- US gallons: 942.5 / 3.785411784 = 249.0 gallons
- Imperial gallons: 942.5 / 4.54609 = 207.3 gallons
The difference is 41.7 gallons, on one ordinary tank. Scale it up and it gets worse. A 500-gallon fuel order means 1,892.7 litres in the US but 2,273.0 litres in imperial terms, a gap of 380.3 litres. If your software sizes deliveries, doses chemicals, or bills customers by the gallon, the wrong definition is a financial bug, not a cosmetic one.
There is a useful sanity check here: 1,000 litres is 264.17 US gallons but only 219.97 imperial gallons. If a conversion in your app ever returns a number near 264 for a British user, you have found the bug.
The fix: make the unit part of the API
The root cause is an unnamed constant carrying an unstated assumption. The fix is to make the gallon variant explicit everywhere it matters:
const LITRES_PER_GALLON = {
US: 3.785411784,
IMPERIAL: 4.54609,
};
function litresToGallons(litres, system) {
const factor = LITRES_PER_GALLON[system];
if (!factor) throw new Error(`Unknown gallon system: ${system}`);
return litres / factor;
}
litresToGallons(942.5, 'US'); // 249.0
litresToGallons(942.5, 'IMPERIAL'); // 207.3
Three design choices do the heavy lifting:
- Named constants in one place. Both definitions live in a single object. Nobody copy-pastes a bare 3.785 ever again.
-
A required parameter, not a default. If
systemis optional, someone will forget it, and the default will be wrong for half your users. Force the caller to decide. (If you must default, default from the user's locale, and log it.) -
Fail loudly on unknown values. A typo like
'UK'should throw, not silently fall through to whichever branch happens to catch it.
If you are building or checking a reference implementation, a public cylinder volume calculator shows the expected behaviour: dimensions in, volume out, with the unit system chosen explicitly by the user rather than assumed by the code.
Lock it down with tests
Unit bugs survive because conversions are rarely tested with real-world values. Two test cases with known answers would have caught the gallon bug on day one:
test('1000 litres in US gallons', () => {
expect(litresToGallons(1000, 'US')).toBeCloseTo(264.17, 2);
});
test('1000 litres in imperial gallons', () => {
expect(litresToGallons(1000, 'IMPERIAL')).toBeCloseTo(219.97, 2);
});
Add a round-trip test as well: converting litres to gallons and back should return the starting value in both systems. And whenever you write a conversion test, take the expected value from a published reference (a cylinder volume in litres reference table, for example) instead of computing it with the same constant you are testing. A test that derives its expectation from the code under test proves nothing.
Can you detect the right gallon automatically?
A tempting shortcut is to infer the system from the user's locale: en-US gets US gallons, en-GB gets imperial, done. Locale detection is a reasonable default, but treat it as a hint, not a verdict:
-
Expats and travellers routinely run devices in a foreign locale. An American engineer working in Dubai may have an
en-GBbrowser and US expectations. - Canada is genuinely mixed. Officially metric, but US gallons dominate in practice near the border, while older imperial references linger in some industries. No locale string resolves that.
- Shared machines and screenshots in industrial settings mean the person reading the number is often not the person whose locale configured it.
The robust pattern is: default from locale, display the active variant next to every result ("249.0 gal (US)"), and let the user switch it in one tap, remembering their choice. The label matters as much as the default. Half of the gallon bug's damage comes from users never learning that a choice existed. A bare number invites trust; a labelled number invites a second look, and that second look is what catches the mismatch before it reaches an invoice.
Other traps in the same family
The gallon split is the famous one, but it has relatives worth knowing:
- The US dry gallon is 4.4048838 litres, a third definition, used for dry goods. Rare in software, devastating when it appears.
- Cubic feet conversions inherit the ambiguity: 1 ft³ = 7.48052 US gallons but 6.22884 imperial gallons. If your ft³-to-gallons constant came from an American source and your users are British, the same 20 percent error rides along.
- Miles per gallon comparisons between US and UK car reviews are meaningless without the variant. A car rated 40 mpg (imperial) achieves about 33.3 mpg (US). Automotive content that ignores this misleads readers on two continents.
- Fluid ounces differ too: the US fluid ounce is 29.5735 mL, the imperial is 28.4131 mL. Smaller stakes, same disease.
A one-line audit for your own codebase
Before the next deploy, grep your codebase for bare conversion numbers and ask one question about each: which definition is this?
grep -rn "3.785\|4.546\|264.17\|219.97\|7.48052\|6.22884" src/
Every bare match is a candidate gallon bug. For each hit, check three things: is the constant named, is the variant explicit in the function signature or the UI label, and is there a test with a known real-world value? Most codebases fail at least one of the three on the first audit. The ones that fail all three are usually the ones with a confused user in London and a calm user in Texas, and neither of them can tell you what is wrong.
The rule to take away
Any unit with a regional variant is a parameter, not a constant. Gallons, fluid ounces, tons (short, long, metric), and even calories all have multiple live definitions. When you catch yourself typing a bare conversion factor into a function, stop and ask: which definition is this, who decided, and where would a user tell me otherwise?
Your London user and your Texas user are both right. Your code just has to know which gallon each of them means.

Top comments (0)