I'd like to create a static lookup table for powers of ten returned as type boost::mulitprecision's uint256_t
:
static uint256_t power_ten(const uint8_t n)
{
static const uint256_t table[] =
{
1,
10
// etc
};
return table[n];
}
However, I won't be able to define the literals for 256 bits because C++ literals are int
s.
Consequently I thought I could use template metaprogramming to calculate the powers so I don't need to specify literals, similar to this Fibonacci example I found:
#include <boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;
using Integer = int; // Works
//using Integer = uint256_t; // Causes compiler errors
template<Integer n>
struct fibonacci
{
static constexpr Integer value = fibonacci<n-1>::value + fibonacci<n-2>::value;
};
template<>
struct fibonacci<0>
{
static constexpr Integer value = 0;
};
template<>
struct fibonacci<1>
{
static constexpr Integer value = 1;
};
int main()
{
Integer a = fibonacci<40>::value;
std::cout << a << std::endl;
}
This code works for int
. However, if I comment/uncomment to use uint256_t
I get these compiler errors:
clang-14: warning: -lquadmath: 'linker' input unused [-Wunused-command-line-argument]
<source>:8:18: error: a non-type template parameter cannot have type 'Integer' (aka 'number<cpp_int_backend<256, 256, unsigned_magnitude, unchecked, void>>')
template<Integer n>
^
<source>:11:28: error: declaration of constexpr static data member 'value' requires an initializer
static constexpr Integer value = fibonacci<n-1>::value + fibonacci<n-2>::value;
^
2 errors generated.
ASM generation compiler returned: 1
<source>:8:18: error: a non-type template parameter cannot have type 'Integer' (aka 'number<cpp_int_backend<256, 256, unsigned_magnitude, unchecked, void>>')
template<Integer n>
^
<source>:11:28: error: declaration of constexpr static data member 'value' requires an initializer
static constexpr Integer value = fibonacci<n-1>::value + fibonacci<n-2>::value;
https://godbolt.org/z/qsK8rG5WG
Is there another way to achieve this?
UPDATE
I've just tried populating the static table using uint256_t
with a string constructor, as per the comments. This seemed to work but once I reach:
uint256_t("1000000000000000000000000000000000000000000000000000000000000000000000000000000")
std::cout
outputs the wrong value:
73663286101470436611432119930496737173840122674875487684339327936694962880512
Instead of using costly string initialization, consider an expression that is likely to be more efficient:
#include <boost/multiprecision/cpp_int.hpp>
using boost::multiprecision::uint512_t;
static uint512_t power_ten(const uint8_t n)
{
static const uint512_t table[] = {
pow(uint512_t(10), 0), pow(uint512_t(10), 1), pow(uint512_t(10), 2), pow(uint512_t(10), 3),
pow(uint512_t(10), 4), pow(uint512_t(10), 5), pow(uint512_t(10), 6), pow(uint512_t(10), 7),
pow(uint512_t(10), 8), pow(uint512_t(10), 9), pow(uint512_t(10), 10), pow(uint512_t(10), 11),
pow(uint512_t(10), 12), pow(uint512_t(10), 13), pow(uint512_t(10), 14), pow(uint512_t(10), 15),
pow(uint512_t(10), 16), pow(uint512_t(10), 17), pow(uint512_t(10), 18), pow(uint512_t(10), 19),
pow(uint512_t(10), 20), pow(uint512_t(10), 21), pow(uint512_t(10), 22), pow(uint512_t(10), 23),
pow(uint512_t(10), 24), pow(uint512_t(10), 25), pow(uint512_t(10), 26), pow(uint512_t(10), 27),
pow(uint512_t(10), 28), pow(uint512_t(10), 29), pow(uint512_t(10), 30), pow(uint512_t(10), 31),
pow(uint512_t(10), 32), pow(uint512_t(10), 33), pow(uint512_t(10), 34), pow(uint512_t(10), 35),
pow(uint512_t(10), 36), pow(uint512_t(10), 37), pow(uint512_t(10), 38), pow(uint512_t(10), 39),
pow(uint512_t(10), 40), pow(uint512_t(10), 41), pow(uint512_t(10), 42), pow(uint512_t(10), 43),
pow(uint512_t(10), 44), pow(uint512_t(10), 45), pow(uint512_t(10), 46), pow(uint512_t(10), 47),
pow(uint512_t(10), 48), pow(uint512_t(10), 49), pow(uint512_t(10), 50), pow(uint512_t(10), 51),
pow(uint512_t(10), 52), pow(uint512_t(10), 53), pow(uint512_t(10), 54), pow(uint512_t(10), 55),
pow(uint512_t(10), 56), pow(uint512_t(10), 57), pow(uint512_t(10), 58), pow(uint512_t(10), 59),
pow(uint512_t(10), 60), pow(uint512_t(10), 61), pow(uint512_t(10), 62), pow(uint512_t(10), 63),
pow(uint512_t(10), 64), pow(uint512_t(10), 65), pow(uint512_t(10), 66), pow(uint512_t(10), 67),
pow(uint512_t(10), 68), pow(uint512_t(10), 69), pow(uint512_t(10), 70), pow(uint512_t(10), 71),
pow(uint512_t(10), 72), pow(uint512_t(10), 73), pow(uint512_t(10), 74), pow(uint512_t(10), 75),
pow(uint512_t(10), 76), pow(uint512_t(10), 77), pow(uint512_t(10), 78), pow(uint512_t(10), 79),
pow(uint512_t(10), 80), pow(uint512_t(10), 81), pow(uint512_t(10), 82), pow(uint512_t(10), 83),
pow(uint512_t(10), 84), pow(uint512_t(10), 85), pow(uint512_t(10), 86), pow(uint512_t(10), 87),
pow(uint512_t(10), 88), pow(uint512_t(10), 89), pow(uint512_t(10), 90), pow(uint512_t(10), 91),
pow(uint512_t(10), 92), pow(uint512_t(10), 93), pow(uint512_t(10), 94), pow(uint512_t(10), 95),
pow(uint512_t(10), 96), pow(uint512_t(10), 97), pow(uint512_t(10), 98), pow(uint512_t(10), 99),
pow(uint512_t(10), 100),
// etc
};
return table[n];
}
int main() {
for (auto n = 0; n<100; ++n)
std::cout << power_ten(n) << "\n";
}
Prints
1
10
100
1000
10000
100000
...
100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Even cleaner, make it procedural:
static uint512_t power_ten(const uint8_t n) {
static auto const table = [] {
std::array<uint512_t, 155> tmp;
for(int exp = 0; auto& el : tmp)
el = pow(uint512_t(10), exp++);
return tmp;
}();
return table.at(n); // or [n]
}
Printing the same and more.