DevToys Web Pro iconDevToys Web ProBlog
Evaluează-ne:
Încearcă extensia de browser:
← Back to Blog

Unit Converter Guide: Base Units, Floating Point, and Temperature

10 min read

Unit conversion looks deceptively simple: multiply by a factor and you are done. In practice the naive approach accumulates floating-point error when you chain conversions, breaks entirely for temperature because Celsius and Fahrenheit have an offset not just a scale, and leads to silent bugs when binary and decimal interpretations of "kilobyte" are mixed. Use the Unit Converter to follow along with the examples in this guide.

This guide covers the base-unit normalization strategy that eliminates chained-conversion error, the affine formulas that temperature conversion requires, the binary-vs-decimal data size divide, the main unit categories developers encounter, precision display, and concrete developer scenarios where getting units wrong has real consequences.

Unit Categories Developers Encounter

A general-purpose unit converter works across several categories. Each has a natural base unit that everything else reduces to:

CategorySI base unitCommon conversions
Lengthmetre (m)km, cm, mm, inch, foot, yard, mile
Mass / weightkilogram (kg)gram, tonne, pound, ounce, stone
Temperaturekelvin (K)Celsius, Fahrenheit — offset required
Volumelitre (L) or cubic metremL, fluid oz, cup, gallon, pint
Speedmetres per second (m/s)km/h, mph, knots, ft/s
Data sizebit or byteKB/KiB, MB/MiB, GB/GiB (see below)
Timesecond (s)ms, minutes, hours, days, weeks

Keeping this table in mind matters when you implement conversions in code. Reducing every value to the base unit first, and then scaling to the target, is the key to avoiding accumulated rounding error — covered in detail in the next section.

Base-Unit Normalization: Avoiding Floating-Point Chains

The naive approach to building a unit converter is to write a direct formula for every pair of units: inches to centimetres, centimetres to millimetres, millimetres to feet, and so on. With n units you need on the order of formulas, and each chained multiplication introduces a fresh rounding error.

The correct approach is to store one canonical factor per unit — how many base units one of that unit equals — and always convert via the base unit in two steps:

  1. Input → base unit: multiply the value by the input unit's factor.
  2. Base unit → output: divide by the output unit's factor.
// All length factors expressed in metres
const LENGTH = {
  metre:      1,
  kilometre:  1000,
  centimetre: 0.01,
  millimetre: 0.001,
  inch:       0.0254,
  foot:       0.3048,
  yard:       0.9144,
  mile:       1609.344,
  nauticalMile: 1852,
};

function convertLength(value, from, to) {
  const metres = value * LENGTH[from];   // step 1: to base unit
  return metres / LENGTH[to];            // step 2: to target unit
}

// inches to feet — two multiplications, minimal error
console.log(convertLength(72, "inch", "foot"));   // 6
console.log(convertLength(1,  "mile", "kilometre")); // 1.609344

Compare that with the naive chain: inches → centimetres → millimetres → feet. Each intermediate step multiplies floating-point error. With the base-unit strategy you always do exactly two floating-point operations regardless of how exotic the path is.

Why floating-point error accumulates in chains

IEEE 754 double-precision floats represent most decimal fractions as repeating binary fractions. The number 0.1 in JavaScript is actually 0.1000000000000000055511151231257827021181583404541015625. Multiplying several such numbers together compounds the error. The base-unit approach keeps the chain at exactly two operations, so the maximum rounding error is bounded and predictable.

// Naive chained: inch -> cm -> mm -> feet
const inchToCm   = 2.54;
const cmToMm     = 10;
const mmToFoot   = 0.00328084;

const naive = 72 * inchToCm * cmToMm * mmToFoot;
console.log(naive); // 5.999999... instead of 6

// Base-unit strategy: always exact within float precision
console.log(convertLength(72, "inch", "foot")); // 6

Temperature: Affine Formulas, Not Just Scale Factors

Temperature conversion is the most common place unit-converter implementations go wrong. Celsius, Fahrenheit, and Kelvin do not share a common zero point, so you cannot convert between them with a single multiplication. You need an affine transformation: a scale and an offset.

From → ToFormulaExample
Celsius → FahrenheitF = C × 9/5 + 32100 °C = 212 °F
Fahrenheit → CelsiusC = (F − 32) × 5/932 °F = 0 °C
Celsius → KelvinK = C + 273.150 °C = 273.15 K
Kelvin → CelsiusC = K − 273.15373.15 K = 100 °C
Fahrenheit → KelvinK = (F − 32) × 5/9 + 273.15212 °F = 373.15 K

Notice that every formula has both a multiplier and an additive offset. If you omit the offset and just multiply by a scale factor, your conversion will be wrong for every value except absolute zero. This is the single most frequent bug in hand-rolled temperature conversion code.

// WRONG: temperature is not just a scale
function badCelsiusToFahrenheit(c) {
  return c * (9 / 5);  // missing + 32
}
console.log(badCelsiusToFahrenheit(100)); // 180 — should be 212

// CORRECT: affine transformation
function celsiusToFahrenheit(c) {
  return c * (9 / 5) + 32;
}
function fahrenheitToCelsius(f) {
  return (f - 32) * (5 / 9);
}
function celsiusToKelvin(c) {
  return c + 273.15;
}
function kelvinToCelsius(k) {
  return k - 273.15;
}

// Universal temperature converter via Kelvin as base unit
function toKelvin(value, unit) {
  if (unit === "celsius")    return value + 273.15;
  if (unit === "fahrenheit") return (value - 32) * (5 / 9) + 273.15;
  if (unit === "kelvin")     return value;
  throw new Error(`Unknown temperature unit: ${unit}`);
}

function fromKelvin(kelvin, unit) {
  if (unit === "celsius")    return kelvin - 273.15;
  if (unit === "fahrenheit") return (kelvin - 273.15) * (9 / 5) + 32;
  if (unit === "kelvin")     return kelvin;
  throw new Error(`Unknown temperature unit: ${unit}`);
}

function convertTemperature(value, from, to) {
  return fromKelvin(toKelvin(value, from), to);
}

console.log(convertTemperature(100, "celsius",    "fahrenheit")); // 212
console.log(convertTemperature(32,  "fahrenheit", "celsius"));    // 0
console.log(convertTemperature(0,   "celsius",    "kelvin"));     // 273.15

The pattern mirrors the base-unit strategy: convert to Kelvin first, then convert from Kelvin to the target. Each direction requires its own offset arithmetic — you cannot factor out a single multiplier table for temperatures.

Data Size: Binary vs Decimal — Where Each Is Correct

Data size units are the source of persistent confusion because two competing standards use the same prefixes with different meanings. The difference reaches 7.4% at the gigabyte level and grows with each prefix step.

UnitSystemBytesWhere used
KB (kilobyte)Decimal (SI)1,000Storage marketing, network bandwidth, HTTP headers
KiB (kibibyte)Binary (IEC)1,024RAM, OS file sizes, kernel buffers
MB (megabyte)Decimal1,000,000Hard disk capacity, transfer rates
MiB (mebibyte)Binary1,048,576Process memory, JVM heap, container limits
GB (gigabyte)Decimal1,000,000,000Disk specs, cloud storage pricing
GiB (gibibyte)Binary1,073,741,824Kubernetes memory limits, OS reports

Storage manufacturers use decimal prefixes (1 GB = 1,000,000,000 bytes) because the numbers look larger. Operating systems historically used binary math and reported the same disk as smaller. When you set a Kubernetes memory limit of 1Gi you mean 1,073,741,824 bytes. When an S3 bucket shows you "1.2 GB transferred" it means 1,200,000,000 bytes. Mixing them silently is a real operational hazard.

For data-size conversions see also the Data Size Converter, which handles both binary (IEC) and decimal (SI) prefixes explicitly.

// Data size conversion — always be explicit about the base
const BYTES = {
  // Decimal (SI)
  KB:  1_000,
  MB:  1_000_000,
  GB:  1_000_000_000,
  TB:  1_000_000_000_000,
  // Binary (IEC)
  KiB: 1_024,
  MiB: 1_048_576,
  GiB: 1_073_741_824,
  TiB: 1_099_511_627_776,
};

function convertDataSize(value, from, to) {
  const bytes = value * BYTES[from];
  return bytes / BYTES[to];
}

// A 500 GB drive advertised in decimal
const driveBytes = 500 * BYTES.GB;
console.log(driveBytes / BYTES.GiB); // ~465.7 GiB — what the OS reports

Developer Use Cases: Config Thresholds, API Limits, Timeouts

Unit conversion is not just a calculator curiosity — it appears in real production code every day:

Config file thresholds

Infrastructure config files often use inconsistent units. One tool expects milliseconds, another seconds, a third accepts human strings like "30s". Keeping a canonical converter function prevents copy-paste errors when migrating values between tools.

const TIME_MS = {
  ms:      1,
  second:  1_000,
  minute:  60_000,
  hour:    3_600_000,
  day:     86_400_000,
};

function toMs(value, unit) {
  return value * TIME_MS[unit];
}

// nginx uses seconds, Redis uses milliseconds, cron uses minutes
const SESSION_TIMEOUT_MS = toMs(30, "minute"); // 1_800_000 ms
const NGINX_TIMEOUT_S    = SESSION_TIMEOUT_MS / TIME_MS.second; // 1800
const REDIS_TTL_MS       = SESSION_TIMEOUT_MS; // already in ms

API rate and size limits

Cloud APIs express limits in different units. AWS Lambda has a 6 MB payload limit (decimal megabytes). Stripe enforces request body sizes in bytes. PostgreSQL's work_mem setting accepts values like '64MB' where MB historically meant MiB in that context. Always read the documentation to confirm which convention applies.

# Python: checking a payload against AWS Lambda's 6 MB limit
LAMBDA_MAX_BYTES = 6 * 1_000_000  # decimal MB, as documented by AWS

def check_payload(data: bytes) -> bool:
    if len(data) > LAMBDA_MAX_BYTES:
        mb = len(data) / 1_000_000
        raise ValueError(f"Payload {mb:.2f} MB exceeds Lambda 6 MB limit")
    return True

Kubernetes and container memory

Kubernetes uses binary suffixes (Ki, Mi, Gi) in resource manifests. Setting the wrong suffix can over-provision or cause OOM kills.

# 512 MiB = 536,870,912 bytes
kubectl set resources deployment/api --limits=memory=512Mi

# 512 MB = 512,000,000 bytes — about 5% less
kubectl set resources deployment/api --limits=memory=512M

Geographic coordinate precision

GPS coordinates are angles measured in degrees. Developers sometimes need to convert between decimal degrees and degrees-minutes-seconds (DMS). One degree of latitude is approximately 111 km; one arcsecond is about 30 metres. Storing coordinates with fewer than five decimal places introduces meaningful positional error in location-sensitive applications.

function decimalToDMS(decimal) {
  const deg = Math.trunc(decimal);
  const minFloat = Math.abs(decimal - deg) * 60;
  const min = Math.trunc(minFloat);
  const sec = (minFloat - min) * 60;
  return { degrees: deg, minutes: min, seconds: sec };
}

console.log(decimalToDMS(48.8566));
// { degrees: 48, minutes: 51, seconds: 23.76 }
// Paris latitude — 48° 51' 23.76"

Precision and Significant Figures

After converting, you usually want to display a rounded result rather than the full floating-point expansion. Two common approaches:

  • Fixed decimal placesvalue.toFixed(4) always shows four digits after the decimal point. Good for sensor readings or currency where the scale is known.
  • Significant figuresvalue.toPrecision(4) keeps four significant digits regardless of magnitude. Better for scientific values that span many orders of magnitude.
const miles = convertLength(42.195, "kilometre", "mile"); // marathon distance
console.log(miles.toFixed(3));      // "26.219"
console.log(miles.toPrecision(5));  // "26.219"

// Very small values — toPrecision is cleaner
const nm = convertLength(1, "inch", "nanometre"); // 25,400,000
console.log(nm.toFixed(0));         // "25400000"
console.log(nm.toPrecision(4));     // "2.540e+7"

// Avoid displaying raw float noise
const noisy = 0.1 + 0.2;           // 0.30000000000000004
console.log(+noisy.toPrecision(10)); // 0.3

For user-facing output, a common pattern is to pick a fixed number of decimal places based on the magnitude: more places for small values, fewer for large ones. The Unit Converter handles this automatically.

Speed and Volume

Speed

Speed is a derived unit (distance divided by time), so the base-unit strategy applies with a combined factor. Store each speed unit as its equivalent in metres per second:

const SPEED_MPS = {
  mps:   1,          // metres per second
  kph:   1 / 3.6,   // kilometres per hour
  mph:   0.44704,    // miles per hour
  knot:  0.514444,   // nautical miles per hour
  fps:   0.3048,     // feet per second
};

function convertSpeed(value, from, to) {
  return value * SPEED_MPS[from] / SPEED_MPS[to];
}

console.log(convertSpeed(100, "kph", "mph").toFixed(2)); // "62.14"
console.log(convertSpeed(1,   "knot", "kph").toFixed(4)); // "1.8520"

Volume

Volume conversions bridge the metric and US customary systems, where "fluid ounce", "cup", and "pint" all have different sizes in UK vs US conventions. Always confirm which regional standard applies before converting cooking or pharmaceutical measurements.

const VOLUME_LITRES = {
  litre:        1,
  millilitre:   0.001,
  cubicMetre:   1000,
  // US customary
  usFluidOz:    0.0295735,
  usCup:        0.236588,
  usPint:       0.473176,
  usGallon:     3.78541,
  // UK / Imperial
  ukFluidOz:    0.0284131,
  ukPint:       0.568261,
  ukGallon:     4.54609,
};

function convertVolume(value, from, to) {
  return value * VOLUME_LITRES[from] / VOLUME_LITRES[to];
}

// 1 US gallon in litres
console.log(convertVolume(1, "usGallon", "litre").toFixed(5)); // "3.78541"

If data-size conversions are your main concern, the Data Size Converter Guide goes deeper on binary vs decimal prefixes, IEC standard naming, and common pitfalls when comparing disk manufacturer specs with OS-reported sizes.


Convert between length, mass, temperature, speed, volume, time, and data sizes instantly with the Unit Converter — runs entirely in your browser so nothing leaves your machine. For binary vs decimal data-size precision, use the dedicated Data Size Converter.