Unit conversion maps a quantity from one unit to another without changing the physical quantity. Reliable software must preserve the dimension, use an authoritative factor or affine formula, distinguish regional conventions, and apply an explicit precision and rounding policy. A numerically plausible result can still be wrong when the source unit, dimension, or convention is wrong.

The 2019 SI Redefinition

On May 20, 2019, the International System of Units was redefined. All seven base units are now defined by fixing the numerical values of fundamental physical constants:

Base unit Defined by Fixed constant value
Second (s) Cesium-133 hyperfine transition Δν_Cs = 9,192,631,770 Hz
Meter (m) Speed of light c = 299,792,458 m/s
Kilogram (kg) Planck constant h = 6.62607015 × 10⁻³⁴ J·s
Ampere (A) Elementary charge e = 1.602176634 × 10⁻¹⁹ C
Kelvin (K) Boltzmann constant k = 1.380649 × 10⁻²³ J/K
Mole (mol) Avogadro constant N_A = 6.02214076 × 10²³ mol⁻¹
Candela (cd) Luminous efficacy K_cd = 683 lm/W

The key change: the kilogram is no longer defined by a physical artifact (the International Prototype Kilogram in Paris). It's now defined by the Planck constant. This means any laboratory with the right equipment can independently realize the kilogram to full precision.

Why This Matters for Software

Many relationships among SI units and some legally defined non-SI conversions are exact, but the 2019 redefinition did not make every conversion factor exact. Measured, conventional, environmental, and context-dependent quantities can remain approximate. Examples of exact definitions include:

  • 1 inch = exactly 25.4 mm (by definition since 1959)
  • 1 pound = exactly 0.45359237 kg (by definition)
  • °C = K − 273.15 (exact offset)

Even when a factor is exact, software can introduce representation and rounding error. When a factor itself is measured or conventional, both coefficient uncertainty and numeric representation matter.

Floating-Point Precision in Conversion Chains

The Problem

python
# Seemingly correct round-trip conversion
meters = 1.0
feet = meters / 0.3048    # 3.280839895013123
back_to_meters = feet * 0.3048  # 0.9999999999999999 (not 1.0!)

This isn't a conversion factor error — it's IEEE 754 binary floating-point representation. The number 0.3048 cannot be represented exactly in binary.

Chain Conversion Accumulates Error

python
# Converting through intermediate units accumulates error
km = 1.0
miles = km / 1.60934
yards = miles * 1760
feet = yards * 3
inches = feet * 12
cm = inches * 2.54
back_to_km = cm / 100000

# Result: 0.9999999406318768 (error: ~6 × 10⁻⁸)

Mitigation Strategies

1. Convert through base unit only (hub-and-spoke):

python
def convert(value: float, from_unit: str, to_unit: str) -> float:
    # Always convert from_unit → base → to_unit (one multiplication, one division)
    to_base = {'km': 1000, 'm': 1, 'cm': 0.01, 'ft': 0.3048, 'in': 0.0254}
    base_value = value * to_base[from_unit]
    return base_value / to_base[to_unit]

2. Use exact rational arithmetic for critical applications:

python
from fractions import Fraction

# Exact: 1 inch = 254/10000 meters = 127/5000 meters
INCH_TO_METER = Fraction(254, 10000)
FOOT_TO_METER = INCH_TO_METER * 12  # Fraction(3048, 10000)

# Round-trip is exact
meters = Fraction(1)
feet = meters / FOOT_TO_METER
back = feet * FOOT_TO_METER  # Fraction(1, 1) — exactly 1

3. Use Decimal for currency-adjacent conversions:

python
from decimal import Decimal

# Weight pricing: $4.99/lb, customer orders 2.5 kg
price_per_lb = Decimal('4.99')
weight_kg = Decimal('2.5')
kg_to_lb = Decimal('2.20462')
total = price_per_lb * weight_kg * kg_to_lb  # Decimal('27.527505')

Dimensional Analysis: Catching Unit Bugs at Design Time

Dimensional analysis is the principle that physical equations must be dimensionally consistent. Using it as a design technique catches unit bugs before they compile.

The Mars Climate Orbiter

In 1999, NASA lost the Mars Climate Orbiter after a navigation interface mismatch contributed to an unintended atmospheric trajectory:

  • Lockheed Martin's ground software output impulse in pound-force seconds (lbf·s)
  • NASA's navigation system expected newton seconds (N·s)
  • The factor of 4.45 difference caused the spacecraft to enter Mars' atmosphere at the wrong altitude

The lesson is broader than one function signature: interface requirements, verification, end-to-end testing, and unit-aware data contracts all failed to prevent the mismatch. Typed quantities can remove one important class of error, but they do not replace systems engineering.

Dimensional Analysis as Code

The key insight: if units are tracked in the type system, unit mismatches become compile errors, not runtime bugs.

code
velocity = distance / time     ✓ [m/s] = [m] / [s]
force = mass × acceleration    ✓ [N] = [kg] × [m/s²]
energy = force × distance      ✓ [J] = [N] × [m]

If you accidentally add meters to seconds, a dimensionally-typed system rejects it at compile time.

Type-Safe Units in Programming

F# Units of Measure (Compile-Time, Zero Runtime Cost)

fsharp
[<Measure>] type m      // meter
[<Measure>] type s      // second
[<Measure>] type kg     // kilogram

let distance = 100.0<m>
let time = 9.58<s>
let speed = distance / time  // inferred type: float<m/s>

// Compile error: cannot add meters and seconds
// let nonsense = distance + time

let mass = 70.0<kg>
let acceleration = 9.81<m/s^2>
let force = mass * acceleration  // float<kg m/s^2> = Newton

F# erases units at compile time — zero runtime overhead, full type safety.

Rust: Phantom Types (uom crate)

rust
use uom::si::f64::*;
use uom::si::length::{meter, foot};
use uom::si::time::second;

let distance = Length::new::<meter>(100.0);
let time = Time::new::<second>(9.58);
let speed = distance / time;  // Velocity type

// Convert to different units
let feet = distance.get::<foot>();  // 328.084...

// Compile error: cannot add Length and Time
// let nonsense = distance + time;

Python: Pint (Runtime Checking)

python
import pint

ureg = pint.UnitRegistry()

distance = 100 * ureg.meter
time = 9.58 * ureg.second
speed = distance / time  # 10.44 meter / second

# Automatic conversion
speed_mph = speed.to(ureg.mile / ureg.hour)
print(speed_mph)  # 23.35 mile / hour

# Runtime error: cannot add incompatible dimensions
# distance + time  → DimensionalityError

TypeScript: Branded Types (Lightweight Approach)

typescript
type Meters = number & { readonly __brand: 'meters' };
type Feet = number & { readonly __brand: 'feet' };
type Seconds = number & { readonly __brand: 'seconds' };

function metersToFeet(m: Meters): Feet {
  return (m * 3.28084) as Feet;
}

function speed(distance: Meters, time: Seconds): number {
  return distance / time;
}

const d = 100 as Meters;
const t = 10 as Seconds;
const v = speed(d, t);

// Type error with strict checking:
// speed(d as Meters, d as Meters)  ← won't catch this with branded types alone

Branded types provide partial safety — they prevent passing feet where meters are expected, but don't validate dimensional equations.

Binary vs Decimal Prefixes

The Confusion

Prefix SI (decimal) Binary (IEC/IEEE 1541)
kilo/kibi 1 KB = 1,000 bytes 1 KiB = 1,024 bytes
mega/mebi 1 MB = 1,000,000 bytes 1 MiB = 1,048,576 bytes
giga/gibi 1 GB = 1,000,000,000 bytes 1 GiB = 1,073,741,824 bytes
tera/tebi 1 TB = 10¹² bytes 1 TiB = 2⁴⁰ bytes

Who Uses Which

Context Convention Example
Hard drive manufacturers Decimal (SI) "1 TB" = 1,000,000,000,000 bytes
RAM specifications Binary "16 GB" actually = 16 GiB = 17,179,869,184 bytes
macOS (since 10.6) Decimal Displays "500 GB" as 500,000,000,000 bytes
Windows Binary Displays "476 GB" for the same drive
Linux (most tools) Binary df -h shows GiB but labels it "G"
Network speeds Decimal "1 Gbps" = 1,000,000,000 bits/second

The Software Problem

python
# "How much free space do I have?"
# Same drive, different answers:
disk_bytes = 500_107_862_016

# macOS / drive manufacturer (decimal)
disk_gb_decimal = disk_bytes / 1_000_000_000  # 500.1 GB

# Windows / most Linux tools (binary)
disk_gib = disk_bytes / (1024 ** 3)  # 465.8 GiB (displayed as "465.8 GB")

# The "missing" 34 GB is purely a labeling difference

Correct Implementation

python
def format_bytes(size: int, binary: bool = True) -> str:
    if binary:
        units = ['B', 'KiB', 'MiB', 'GiB', 'TiB']
        base = 1024
    else:
        units = ['B', 'KB', 'MB', 'GB', 'TB']
        base = 1000
    
    for unit in units:
        if abs(size) < base:
            return f"{size:.1f} {unit}"
        size /= base
    return f"{size:.1f} PiB" if binary else f"{size:.1f} PB"

Reference Tables

Length

From To Factor (exact where noted)
1 inch centimeter 2.54 (exact by definition)
1 foot meter 0.3048 (exact)
1 yard meter 0.9144 (exact)
1 mile kilometer 1.609344 (exact)
1 nautical mile meter 1852 (exact)

Mass

From To Factor
1 pound (avoirdupois) kilogram 0.45359237 (exact)
1 ounce gram 28.349523125 (exact)
1 troy ounce gram 31.1034768 (exact)
1 stone kilogram 6.35029318
1 short ton (US) kilogram 907.18474 (exact)

Temperature

Conversion Formula Note
°C → °F °F = °C × 9/5 + 32 Exact (rational coefficients)
°F → °C °C = (°F − 32) × 5/9 Exact
°C → K K = °C + 273.15 Exact (by definition)
°F → °R °R = °F + 459.67 Rankine scale

Cross-reference: −40° is the same in both Celsius and Fahrenheit.

Speed

From To Factor
1 km/h m/s 1/3.6 (exact)
1 mph km/h 1.609344 (exact)
1 knot km/h 1.852 (exact)
Mach 1 m/s ~343 (varies with temperature and altitude)
1 c (light speed) m/s 299,792,458 (exact, defines the meter)

Common Conversion Pitfalls

1. US vs Imperial Gallons

code
US gallon = 3.785411784 liters
Imperial gallon = 4.54609 liters

Difference: 20%! A "gallon" in UK recipes is 20% larger than US.

2. Troy vs Avoirdupois Ounces

code
Troy ounce (gold, silver) = 31.1 g
Avoirdupois ounce (everything else) = 28.35 g

1 troy pound = 12 troy ounces = 373.2 g
1 avoirdupois pound = 16 ounces = 453.6 g

3. Calorie vs kilocalorie

code
1 calorie (cal) = 4.184 joules (energy to heat 1g water by 1°C)
1 Calorie (Cal) = 1 kilocalorie = 4184 joules

Food labels use "Calories" (capital C) = kilocalories

4. Short Ton vs Long Ton vs Metric Ton

code
Short ton (US) = 2000 lb = 907.185 kg
Long ton (UK) = 2240 lb = 1016.047 kg
Metric ton (tonne) = 1000 kg = 2204.6 lb

Use the Converter

For a quick browser calculation, the Unit Converter covers length, mass, temperature, area, volume, speed, time, binary storage, pressure, energy, power, and force. It displays rounded results and identifies US liquid and IEC binary units; use the unit conversion glossary for the concise definition. Safety-critical, regulated, trade, dosing, and laboratory work still require the applicable standard and a validated calculation path.

Primary Sources

Summary

Unit conversion in software is not just table lookup — it's a type safety problem:

  1. Some conversion factors are exact, while measured or context-dependent values may not be; floating-point representation can add error in either case
  2. Hub-and-spoke conversion (always through base unit) minimizes accumulated error
  3. Dimensional analysis catches unit bugs at design time — the Mars Climate Orbiter proved the cost of ignoring it
  4. Type-safe unit systems (F# units of measure, Rust uom, Python Pint) make unit mismatches impossible or detectable
  5. Binary vs decimal prefixes remain a persistent source of user confusion — always specify which convention you're using
  6. Same-name units differ by region (US vs Imperial gallon, troy vs avoirdupois ounce) — context determines interpretation