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 <ctype.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#define MAX_LIMBS 128 /* 2^4096 — beyond any realistic tool input. */
static const char DIGITS[] = "0123456789abcdefghijklmnopqrstuvwxyz";
/* Arbitrary-precision integer: little-endian base-2^32 limbs; len 0 == zero. */
typedef struct { bool neg; int len; uint32_t limb[MAX_LIMBS]; } Big;

/* b = b * mul + add — one Horner step over the limbs. */
static void big_mul_add(Big *b, uint32_t mul, uint32_t add) {
    uint64_t carry = add;
    for (int i = 0; i < b->len; i++) {
        uint64_t t = (uint64_t)b->limb[i] * mul + carry;
        b->limb[i] = (uint32_t)t, carry = t >> 32;
    }
    while (carry) b->limb[b->len++] = (uint32_t)carry, carry >>= 32;
}

/* b /= d; returns b % d (used to peel least-significant digits). */
static uint32_t big_divmod(Big *b, uint32_t d) {
    uint64_t rem = 0;
    for (int i = b->len - 1; i >= 0; i--) {
        uint64_t cur = rem << 32 | b->limb[i];
        b->limb[i] = (uint32_t)(cur / d), rem = cur % d;
    }
    while (b->len > 0 && b->limb[b->len - 1] == 0) b->len--;
    return (uint32_t)rem;
}
static int digit_value(int c) {
    if (c >= '0' && c <= '9') return c - '0';
    if (c >= 'a' && c <= 'z') return c - 'a' + 10;
    return -1;
}

/* Parse `s` in `base`; false on invalid input or overflow. Sign honored;
 * radix prefix stripped only when it matches the base. */
static bool nb_parse(const char *s, int base, Big *out) {
    if (base < 2 || base > 36) return false;
    memset(out, 0, sizeof *out);
    while (isspace((unsigned char)*s)) s++;
    const char *e = s + strlen(s);
    while (e > s && isspace((unsigned char)e[-1])) e--;
    if (s < e && (*s == '-' || *s == '+')) out->neg = *s++ == '-';
    if (e - s >= 2 && *s == '0') {
        char p = (char)tolower((unsigned char)s[1]);
        if ((p == 'x' && base == 16) || (p == 'b' && base == 2) || (p == 'o' && base == 8)) s += 2;
    }
    if (s >= e) return false;
    for (; s < e; s++) {
        int d = digit_value(tolower((unsigned char)*s));
        if (d < 0 || d >= base || out->len > MAX_LIMBS - 2) return false;
        big_mul_add(out, (uint32_t)base, (uint32_t)d);
    }
    if (out->len == 0) out->neg = false; /* normalize -0 */
    return true;
}

/* Render `v` in `base` into `buf` (needs 33 * MAX_LIMBS + 2 bytes). */
static void nb_format(const Big *v, int base, char *buf) {
    if (base < 2 || base > 36) { *buf = '\0'; return; }
    Big t = *v;
    bool neg = t.neg && t.len > 0;
    char tmp[MAX_LIMBS * 33];
    int n = 0;
    if (t.len == 0) tmp[n++] = '0';
    while (t.len > 0) tmp[n++] = DIGITS[big_divmod(&t, (uint32_t)base)];
    char *p = buf;
    if (neg) *p++ = '-';
    while (n > 0) *p++ = tmp[--n];
    *p = '\0';
}
/* value(from_base) -> to_base; false when it does not parse. */
static bool nb_convert(const char *value, int from, int to, char *out) {
    Big n;
    if (!nb_parse(value, from, &n)) return false;
    nb_format(&n, to, out);
    return true;
}

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 →