The general problem of constructing the base B
numeral that
represents a given unsigned integer at compiletime is quite
interesting. It can be solved concisely by an application of the valuable
technique of building a compiletime std::array
, which goes back
at least to this answer
The function template digits
as defined below returns a compiletime
null-terminated std::array
of char
that contains the base Base
numeral
representing the integer N
, for Base
in the reasonable range [2,36]
(digits '0-9A-Z')
#include <type_traits>
#include <array>
// Get the number of base `base` digits in `n`
constexpr std::size_t base_digits(unsigned long n, unsigned base)
{
return (n / base) == 0 ? 1 : 1 + base_digits(n / base,base);
}
// Convert the remainder `r` of division by some base to a based digit
constexpr char based_digit(unsigned r)
{
return r > 36 ? '!' : r < 10 ? '0' + r : 'A' + r - 10;
}
template <unsigned long N, unsigned Base,
std::size_t Len = base_digits(N,Base) + 1, unsigned ...Rems>
constexpr std::array<char, Len>
digits(std::enable_if_t<(Base > 1 && Base <= 36 && N < Base)> * = nullptr)
{
return std::array<char,Len>{{based_digit(N), Rems...,0}};
}
template <unsigned long N, unsigned Base,
std::size_t Len = base_digits(N,Base) + 1, unsigned ...Rems>
constexpr std::array<char,Len>
digits(std::enable_if_t<(Base > 1 && Base <= 36 && N >= Base)> * = nullptr)
{
return digits<N / Base, Base, Len, based_digit(N % Base), Rems...>();
}
Prepend that to this test:
constexpr auto decimal = digits<9876543210,10>();
constexpr auto hex = digits<9876543210,16>();
constexpr auto octal = digits<123456789,8>();
constexpr auto binary = digits<123456789,2>();
constexpr auto b_36 = digits<36 + 35,36>();
static_assert(decimal.size() == 11,"");
#include <iostream>
#include <cassert>
int main() {
char * pend;
assert(std::strtoul(decimal.data(),&pend,10) == 9876543210);
std::cout << decimal.data() << std::endl;
assert(std::strtoul(hex.data(),&pend,16) == 9876543210);
std::cout << hex.data() << std::endl;
assert(std::strtoul(octal.data(),&pend,8) == 123456789);
std::cout << octal.data() << std::endl;
assert(std::strtoul(binary.data(),&pend,2) == 123456789);
std::cout << binary.data() << std::endl;
assert(std::strtoul(b_36.data(),&pend,36) == 36 + 35);
std::cout << b_36.data() << std::endl;
return 0;
}
Output is:
9876543210
24CB016EA
726746425
111010110111100110100010101
1Z
(gcc 4.9.2/clang 3.5.1, -std=c++14. Trivially adaptable for C++11)