Skip to content

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 →