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.cpp — CosmoDev polyglot showcase port of the
//  `data-unit-converter` tool
//  -----------------------------------------------------------------------------
//  Language : C++ (C++17, 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; convertData returns NaN for non-finite input, unknown
//  unit symbols, or an invalid base — never throws.
// =============================================================================

#include <array>
#include <cmath>
#include <optional>
#include <string_view>

namespace cosmodev {

/// Supported conversion radix: decimal (`Decimal = 1000`, SI units) or binary
/// (`Binary = 1024`, IEC units).
enum class Base {
    Decimal = 1000,
    Binary = 1024,
};

/// Numeric radix as a double, ready for `std::pow`.
constexpr double radix(Base base) noexcept {
    return base == Base::Decimal ? 1000.0 : 1024.0;
}

/// Units available under each base, ordered smallest (B) to largest. The UI
/// uses these to populate the per-base unit pickers.
constexpr std::array<std::string_view, 5> unitsFor(Base base) noexcept {
    return base == Base::Decimal
        ? std::array<std::string_view, 5>{"B", "KB", "MB", "GB", "TB"}
        : std::array<std::string_view, 5>{"B", "KiB", "MiB", "GiB", "TiB"};
}

/// Power-of-base rank for a unit symbol: "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. Unknown symbols miss the
/// table (std::nullopt), mirroring the TS dictionary miss.
constexpr std::optional<int> unitExponent(std::string_view unit) noexcept {
    if (unit == "B") return 0;
    if (unit == "KB" || unit == "KiB") return 1;
    if (unit == "MB" || unit == "MiB") return 2;
    if (unit == "GB" || unit == "GiB") return 3;
    if (unit == "TB" || unit == "TiB") return 4;
    return std::nullopt;
}

/// Convert a value between two digital-data units under the given base.
///
/// Strategy: reduce to bytes via `value × base^fromExp`, then divide by
/// `base^toExp`. Non-finite values (Inf/NaN) or unknown symbols return NaN —
/// never throws (mirrors the TypeScript contract). `Base` is already
/// constrained by its enum type; the int-based overload below preserves the
/// original "invalid base → NaN" behavior for raw numeric bases.
double convertData(double value, std::string_view from, std::string_view to, Base base) noexcept {
    // Reject Inf/NaN early so they don't propagate through the multiplication.
    if (!std::isfinite(value)) return std::nan("");
    // Unknown unit symbols miss the table (nullopt) — treat as invalid.
    const std::optional<int> fromExp = unitExponent(from);
    const std::optional<int> toExp = unitExponent(to);
    if (!fromExp || !toExp) return std::nan("");

    const double r = radix(base);
    return (value * std::pow(r, static_cast<double>(*fromExp))) /
           std::pow(r, static_cast<double>(*toExp));
}

/// Int-based entry point mirroring the TypeScript signature
/// (`convertData(value, fromUnit, toUnit, base)`) where the base arrives as a
/// plain number from the URL/share state. Returns NaN for a base other than
/// 1000/1024 (anything else is a caller bug).
double convertData(double value, std::string_view from, std::string_view to, int base) noexcept {
    if (base != 1000 && base != 1024) return std::nan("");
    return convertData(value, from, to, base == 1000 ? Base::Decimal : Base::Binary);
}

}  // namespace cosmodev

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 →