Skip to content

Number Base Converter — C++ source

Convert numbers between binary, octal, decimal and hexadecimal. BigInt-powered, so it handles arbitrarily large values without precision loss.

This is the C++ implementation — the same logic the interactive tool runs, in a shareable, citable form.

// number-base — C++ port: arbitrary-precision base conversion (bases 2-36).
#include <algorithm>
#include <cctype>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
inline constexpr char kDigits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
// Arbitrary-precision integer: little-endian base-2^32 limbs; empty == zero.
struct Big {
    bool neg = false;
    std::vector<uint32_t> mag;
    // this = this * mul + add — one Horner step over the limbs.
    void mul_add(uint32_t mul, uint32_t add) {
        uint64_t carry = add;
        for (size_t i = 0; i < mag.size(); i++) {
            uint64_t t = (uint64_t)mag[i] * mul + carry;
            mag[i] = (uint32_t)t;
            carry = t >> 32;
        }
        while (carry) mag.push_back((uint32_t)carry), carry >>= 32;
    }
    // this /= d; returns this % d (used to peel least-significant digits).
    uint32_t divmod(uint32_t d) {
        uint64_t rem = 0;
        for (size_t i = mag.size(); i-- > 0;) {
            uint64_t cur = rem << 32 | mag[i];
            mag[i] = (uint32_t)(cur / d);
            rem = cur % d;
        }
        while (!mag.empty() && mag.back() == 0) mag.pop_back();
        return (uint32_t)rem;
    }
};
// Map a lowercase character to its value; -1 when not a digit.
int digit_value(char c) {
    return c >= '0' && c <= '9' ? c - '0' : c >= 'a' && c <= 'z' ? c - 'a' + 10 : -1;
}
// Parse `s` as an integer in `base` (2-36); nullopt on invalid input. A radix
// prefix is stripped only when it agrees with the base.
std::optional<Big> parse_big(const std::string& s, int base) {
    if (base < 2 || base > 36) return std::nullopt;
    size_t lo = s.find_first_not_of(" \t\r\n"), hi = s.find_last_not_of(" \t\r\n");
    if (lo == std::string::npos) return std::nullopt;
    std::string t = s.substr(lo, hi - lo + 1);
    std::transform(t.begin(), t.end(), t.begin(), [](unsigned char c) { return (char)std::tolower(c); });
    Big b;
    if (t[0] == '-' || t[0] == '+') b.neg = t[0] == '-', t = t.substr(1);
    if (t.size() >= 2 && t[0] == '0') {
        char p = t[1];
        if ((p == 'x' && base == 16) || (p == 'b' && base == 2) || (p == 'o' && base == 8))
            t = t.substr(2);
    }
    if (t.empty()) return std::nullopt;
    for (char c : t) {
        int d = digit_value(c);
        if (d < 0 || d >= base) return std::nullopt;
        b.mul_add(base, d);
    }
    if (b.mag.empty()) b.neg = false; // normalize -0
    return b;
}
// Render `v` in `base`; "" for an unsupported base, matching the TS lib.
std::string format_big(const Big& v, int base) {
    if (base < 2 || base > 36) return "";
    Big t = v;
    if (t.mag.empty()) return "0";
    bool neg = t.neg;
    std::string out;
    while (!t.mag.empty()) out += kDigits[t.divmod(base)];
    if (neg) out += '-';
    std::reverse(out.begin(), out.end()); // digits were peeled LSD first
    return out;
}
// value(from_base) -> to_base; nullopt when it does not parse.
std::optional<std::string> convert_base(const std::string& value, int from, int to) {
    auto n = parse_big(value, from);
    if (!n) return std::nullopt;
    return format_big(*n, to);
}

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 →