Data Unit Converter — C source
Convert between digital data units - B, KB/KiB, MB/MiB, GB/GiB, TB/TiB. Switch between decimal (1000) and binary (1024) bases, runs entirely in your browser.
This is the C implementation — the same logic the interactive tool runs, in a shareable, citable form.
/* =============================================================================
* data-unit-converter.c — CosmoDev polyglot showcase port of the
* `data-unit-converter` tool
* -----------------------------------------------------------------------------
* Language : C (C11, standard library only)
* Source: ported from src/lib/dataUnits.ts (the canonical, live TypeScript
* lib); mirrors src/tool-sources/data-unit-converter/{python.py,rust.rs}
* License : display source — part of CosmoDev's polyglot tool pages
* (dev.cosmolabs.org). Shown verbatim alongside the JS/TS/Go/Rust/
* Python ports and the other language ports.
* -----------------------------------------------------------------------------
* Pure digital-data unit conversion. Convert between B, KB/KiB, MB/MiB,
* GB/GiB, TB/TiB: the decimal base 1000 pairs with KB/MB/GB/TB (SI) and the
* binary base 1024 with KiB/MiB/GiB/TiB (IEC). Deterministic and
* side-effect free; convert_data returns NAN for non-finite input, unknown
* unit symbols, or an invalid base — it never aborts.
* =============================================================================
*/
#include <math.h>
#include <stddef.h>
#include <string.h>
/* Units available under each base, ordered smallest (B) to largest. The UI
* uses these to populate the per-base unit pickers. C has no dictionary
* literal, so the two bases live in parallel arrays. */
static const char *const UNITS_DECIMAL[] = {"B", "KB", "MB", "GB", "TB"};
static const char *const UNITS_BINARY[] = {"B", "KiB", "MiB", "GiB", "TiB"};
enum { UNIT_COUNT = 5 };
/* Power-of-base rank for each unit: "B" is rank 0 and each prefix step adds
* 1. KB and KiB are both rank 1 — the unit picks the rank, the base decides
* whether a rank means ×1000 or ×1024. Symbols and ranks share one table so a
* lookup miss is detectable (-1), mirroring the TS dictionary miss. */
static const struct {
const char *symbol;
int exponent;
} UNIT_EXPONENT[] = {
{"B", 0},
{"KB", 1}, {"MB", 2}, {"GB", 3}, {"TB", 4},
{"KiB", 1}, {"MiB", 2}, {"GiB", 3}, {"TiB", 4},
};
enum { UNIT_TABLE_COUNT = (int)(sizeof UNIT_EXPONENT / sizeof UNIT_EXPONENT[0]) };
/* Rank for a unit symbol, or -1 when the symbol is not recognized (the TS
* side returns NaN downstream on a dictionary miss — same contract here). */
static int unit_exponent(const char *symbol)
{
if (symbol == NULL)
return -1;
for (int i = 0; i < UNIT_TABLE_COUNT; i++)
if (strcmp(symbol, UNIT_EXPONENT[i].symbol) == 0)
return UNIT_EXPONENT[i].exponent;
return -1;
}
/* Units available under `base`, ordered smallest to largest; writes the
* element count to *count unless count is NULL. Returns NULL for a base other
* than 1000/1024 (invalid base → no unit list, mirroring the NaN contract). */
const char *const *units_for(int base, size_t *count)
{
if (base == 1000) {
if (count != NULL)
*count = UNIT_COUNT;
return UNITS_DECIMAL;
}
if (base == 1024) {
if (count != NULL)
*count = UNIT_COUNT;
return UNITS_BINARY;
}
return NULL;
}
/* Convert `value` between two digital-data units under the given base.
*
* Strategy: reduce to bytes via value × base^from_exp, then divide by
* base^to_exp. Returns NAN for non-finite input (Inf/NaN), unknown unit
* symbols, or a base other than 1000/1024 — never aborts (mirrors the TS
* contract). */
double convert_data(double value, const char *from_unit, const char *to_unit, int base)
{
/* Reject Inf/NaN early so they don't propagate through the multiplication. */
if (isnan(value) || isinf(value))
return NAN;
/* Only the two canonical bases are valid; anything else is a caller bug. */
if (base != 1000 && base != 1024)
return NAN;
/* Unknown unit symbols miss the table (-1) — treat as invalid. */
int from_exp = unit_exponent(from_unit);
int to_exp = unit_exponent(to_unit);
if (from_exp < 0 || to_exp < 0)
return NAN;
return (value * pow((double)base, (double)from_exp)) /
pow((double)base, (double)to_exp);
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →