现代 C++ 如何创建一个包含 10 的幂的 constexpr 数组?

huangapple go评论59阅读模式
英文:

Modern C++ how to create constexpr array of power of 10?

问题

#include <iostream>
using namespace std;

class FastFtoa {
public:
    // Calculate 10^x and 10^-x for x in the range -38 to 38
    static inline constexpr double pow_10(int x) {
        double result = 1.0;
        if (x >= 0) {
            for (int i = 0; i < x; ++i) {
                result *= 10.0;
            }
        } else {
            for (int i = 0; i < -x; ++i) {
                result /= 10.0;
            }
        }
        return result;
    }

    // Generate constexpr arrays for 10^x and 10^-x
    static inline constexpr double __pos_pow_10[] = {
        pow_10(0),  pow_10(1),  pow_10(2),  pow_10(3),  pow_10(4),  pow_10(5),
        pow_10(6),  pow_10(7),  pow_10(8),  pow_10(9),  pow_10(10), pow_10(11),
        pow_10(12), pow_10(13), pow_10(14), pow_10(15), pow_10(16), pow_10(17),
        pow_10(18), pow_10(19), pow_10(20), pow_10(21), pow_10(22), pow_10(23),
        pow_10(24), pow_10(25), pow_10(26), pow_10(27), pow_10(28), pow_10(29),
        pow_10(30), pow_10(31), pow_10(32), pow_10(33), pow_10(34), pow_10(35),
        pow_10(36), pow_10(37), pow_10(38)
    };

    static inline constexpr double __inv_pow_10[] = {
        1.0 / pow_10(0),  1.0 / pow_10(1),  1.0 / pow_10(2),  1.0 / pow_10(3),
        1.0 / pow_10(4),  1.0 / pow_10(5),  1.0 / pow_10(6),  1.0 / pow_10(7),
        1.0 / pow_10(8),  1.0 / pow_10(9),  1.0 / pow_10(10), 1.0 / pow_10(11),
        1.0 / pow_10(12), 1.0 / pow_10(13), 1.0 / pow_10(14), 1.0 / pow_10(15),
        1.0 / pow_10(16), 1.0 / pow_10(17), 1.0 / pow_10(18), 1.0 / pow_10(19),
        1.0 / pow_10(20), 1.0 / pow_10(21), 1.0 / pow_10(22), 1.0 / pow_10(23),
        1.0 / pow_10(24), 1.0 / pow_10(25), 1.0 / pow_10(26), 1.0 / pow_10(27),
        1.0 / pow_10(28), 1.0 / pow_10(29), 1.0 / pow_10(30), 1.0 / pow_10(31),
        1.0 / pow_10(32), 1.0 / pow_10(33), 1.0 / pow_10(34), 1.0 / pow_10(35),
        1.0 / pow_10(36), 1.0 / pow_10(37), 1.0 / pow_10(38)
    };
};

int main() {
    cout << FastFtoa::__inv_pow_10[37] << " " << FastFtoa::__pos_pow_10[37] << "\n";
    return 0;
}
英文:
#include &lt;iostream&gt;
using namespace std;
class FastFtoa {
public:
static inline constexpr double __inv_pow_10[] = {
1.0,                                      // 0
0.1f,                                      // 1
0.01,                                     // 2
0.001,                                    // 3
0.0001,                                   // 4
0.00001,                                  // 5
0.000001,                                 // 6
0.0000001,                                // 7
0.00000001,                               // 8
0.000000001,                              // 9
0.0000000001,                             // 10
0.00000000001,                            // 11
0.000000000001,                           // 12
0.0000000000001,                          // 13
0.00000000000001,                         // 14
0.000000000000001,                        // 15
0.0000000000000001,                       // 16
0.00000000000000001,                      // 17
0.000000000000000001,                     // 18
0.0000000000000000001,                    // 19
0.00000000000000000001,                   // 20
0.000000000000000000001,                  // 21
0.0000000000000000000001,                 // 22
0.00000000000000000000001,                // 23
0.000000000000000000000001,               // 24
0.0000000000000000000000001,              // 25
0.00000000000000000000000001,             // 26
0.000000000000000000000000001,            // 27
0.0000000000000000000000000001,           // 28
0.00000000000000000000000000001,          // 29
0.000000000000000000000000000001,         // 30
0.0000000000000000000000000000001,        // 31
0.00000000000000000000000000000001,       // 32
0.000000000000000000000000000000001,      // 33
0.0000000000000000000000000000000001,     // 34
0.00000000000000000000000000000000001,    // 35
0.000000000000000000000000000000000001,   // 36
0.00000000000000000000000000000000000001, // 37
0.000000000000000000000000000000000000001 // 38
};
static inline constexpr double __pos_pow_10[] = {
1.0,                                      // 0
10.0,                                     // 1
100.0,                                    // 2
1000.0,                                   // 3
10000.0,                                  // 4
100000.0,                                 // 5
1000000.0,                                // 6
10000000.0,                               // 7
100000000.0,                              // 8
1000000000.0,                             // 9
10000000000.0,                            // 10
100000000000.0,                           // 11
1000000000000.0,                          // 12
10000000000000.0,                         // 13
100000000000000.0,                        // 14
1000000000000000.0,                       // 15
10000000000000000.0,                      // 16
100000000000000000.0,                     // 17
1000000000000000000.0,                    // 18
10000000000000000000.0,                   // 19
100000000000000000000.0,                  // 20
1000000000000000000000.0,                 // 21
10000000000000000000000.0,                // 22
100000000000000000000000.0,               // 23
1000000000000000000000000.0,              // 24
10000000000000000000000000.0,             // 25
100000000000000000000000000.0,            // 26
1000000000000000000000000000.0,           // 27
10000000000000000000000000000.0,          // 28
100000000000000000000000000000.0,         // 29
1000000000000000000000000000000.0,        // 30
10000000000000000000000000000000.0,       // 31
100000000000000000000000000000000.0,      // 32
1000000000000000000000000000000000.0,     // 33
10000000000000000000000000000000000.0,    // 34
100000000000000000000000000000000000.0,   // 35
1000000000000000000000000000000000000.0,  // 36
10000000000000000000000000000000000000.0, // 37
100000000000000000000000000000000000000.0 // 38
};
};
int main()
{
cout &lt;&lt; FastFtoa::__inv_pow_10[37] &lt;&lt; &quot; &quot; &lt;&lt; FastFtoa::__pos_pow_10[37] &lt;&lt; &quot;\n&quot;;
return 0;
}

I need to create constexpr array of 10^x and 10^-x, for x in range <s>-64..64</s> -38..38. It's for fast float to string conversion. It also needs to be inside a static class (all functions are public static).

It's possible to create them manually like in the code above, but it doesn't look good. Not to mention the full float64 range is 10^308, and that will be a very ugly code if it's done manually.

In C++17/20, what's the proper way to do this?

答案1

得分: 3

#include <algorithm>
#include <array>
#include <cmath>
#include <iostream>
#include <utility>

template<typename T, T... ints>
constexpr auto sequence1(std::integer_sequence<T, ints...>)
{
    return std::array{std::pow(10L, ints)...};
}

template<typename T, T... ints>
constexpr auto sequence2(std::integer_sequence<T, ints...>)
{
    return std::array{(1/std::pow(10L, ints))...};
}

int main()
{
    constexpr auto my_arr1 = sequence1(std::make_integer_sequence<size_t, 20>{});
    constexpr auto my_arr2 = sequence2(std::make_integer_sequence<size_t, 20>{});
    std::cout << my_arr1[5] << std::endl;
    std::cout << my_arr2[5] << std::endl;
    return 0;
}
英文:
#include &lt;algorithm&gt;
#include &lt;array&gt;
#include &lt;cmath&gt;
#include &lt;iostream&gt;
#include &lt;utility&gt;
template&lt;typename T, T... ints&gt;
constexpr auto sequence1(std::integer_sequence&lt;T, ints...&gt;)
{
return std::array{std::pow(10L, ints)...};
}
template&lt;typename T, T... ints&gt;
constexpr auto sequence2(std::integer_sequence&lt;T, ints...&gt;)
{
return std::array{(1/std::pow(10L, ints))...};
}
int main()
{
constexpr auto my_arr1 = sequence1(std::make_integer_sequence&lt;size_t, 20&gt;{});
constexpr auto my_arr2 = sequence2(std::make_integer_sequence&lt;size_t, 20&gt;{});
std::cout &lt;&lt; my_arr1[5] &lt;&lt; std::endl;
std::cout &lt;&lt; my_arr2[5] &lt;&lt; std::endl;
return 0;
}

答案2

得分: 2

你可以利用std::arrayconstexpr的事实。只要你有可以在编译时完成的计算,就可以使用这个数组填充计算出来的值。

在这个示例中:make_pow_10_map 创建了一个10的幂的映射。在一个循环中,将1.0f除以10.f,并在另一个循环中将1.0f乘以10.f。所有这些值都存储在一个数组中(你也可以很容易地为2个数组做同样的事情)。

然后,这个编译时数组在一个简单的查找函数中使用,该函数返回预先计算的值。

#include <array>
#include <iostream>
#include <stdexcept>

template<std::size_t N>
static constexpr auto make_pow_10_map()
{
    std::array<float, (2 * N)> values{};

    float value{ 1.0 };

    for (std::size_t n = (N-1); n > 0ul; --n)
    {
        values[n] = value;
        value /= 10.0f;
    }
    values[0] = value;

    value = 1.0;
    for (std::size_t n = (N-1); n < 2*N; ++n)
    {
        values[n] = value;
        value *= 10.0f;
    }

    return values;
}

float pow_10(int n)
{
    constexpr std::size_t N{37ul};
    static constexpr auto powers_of_10 = make_pow_10_map<N>();
    int limit = N;

    if ((n < -limit) || (n > limit)) throw std::invalid_argument{"index out of range"};

    std::size_t index = (powers_of_10.size() / 2ul) + n - 1ul;
    return powers_of_10[index];
}

int main()
{
    std::cout << pow_10(-3) << "\n";
    std::cout << pow_10(0) << "\n";
    std::cout << pow_10(6) << "\n";
    std::cout << pow_10(10) << "\n";
    std::cout << pow_10(11) << "\n";
    return 0;
}
英文:

You can make use of the fact that std::array is constexpr. And as long as you have calculations that can be done at compile time you can fill that array with calculated values.

In this example : make_pow_10_map makes a map of powers of 10. Simply dividing 1.0f by 10.f in a loop, and multiplying 1.0f by 10.f in another loop. All the values are stored in one array (you can easiliy do the same for 2 arrays).

Then this compile time array is used in a simple lookup function that returns precalculated values.

#include &lt;array&gt;
#include &lt;iostream&gt;
#include &lt;stdexcept&gt;
template&lt;std::size_t N&gt;
static constexpr auto make_pow_10_map()
{
std::array&lt;float, (2 * N)&gt; values{};
float value{ 1.0 };
for (std::size_t n = (N-1); n &gt; 0ul; --n)
{
values[n] = value;
value /= 10.0f;
}
values[0] = value;
value = 1.0;
for (std::size_t n = (N-1); n &lt; 2*N; ++n)
{
values[n] = value;
value *= 10.0f;
}
return values;
}
float pow_10(int n)
{
constexpr std::size_t N{37ul};
static constexpr auto powers_of_10 = make_pow_10_map&lt;N&gt;();
int limit = N;
if ((n &lt; -limit) || (n &gt; limit)) throw std::invalid_argument{&quot;index out of range&quot;};
std::size_t index = (powers_of_10.size() / 2ul) + n - 1ul;
return powers_of_10[index];
}
int main()
{
std::cout &lt;&lt; pow_10(-3) &lt;&lt; &quot;\n&quot;;
std::cout &lt;&lt; pow_10(0) &lt;&lt; &quot;\n&quot;;
std::cout &lt;&lt; pow_10(6) &lt;&lt; &quot;\n&quot;;
std::cout &lt;&lt; pow_10(10) &lt;&lt; &quot;\n&quot;;
std::cout &lt;&lt; pow_10(11) &lt;&lt; &quot;\n&quot;;
return 0;
}

答案3

得分: 2

无法提供代码的中文翻译,以下是代码的原文:

One can use an unnamed lambda to initialize the `constexpr std::array` to reduce namespace clutter. `inline` is redundant for static constexprs.

#include <array>
#include <iostream>

class FastFtoa {
public:
    static constexpr size_t N{39};
    static constexpr std::array<float, N> _pos_pow_10 = []() {
        std::array<float, N> x{1};
        for (size_t i = 1; i < N; i++)
            x[i] = 10 * x[i - 1];
        return x;
    }();
    static constexpr std::array<float, N> _inv_pow_10 = []() {
        std::array<float, N> x{1};
        for (size_t i = 1; i < N; i++)
            x[i] = .1f * x[i - 1];
        return x;
    }();
};

int main()
{
    std::cout << FastFtoa::_pos_pow_10[0] << '\n';
    std::cout << FastFtoa::_pos_pow_10[38] << '\n';
    std::cout << FastFtoa::_inv_pow_10[0] << '\n';
    std::cout << FastFtoa::_inv_pow_10[38] << '\n';
}

Output:

1
1e+38
1
1e-38
英文:

One can use an unnamed lambda to initialize the constexpr std::array to reduce namespace clutter. inline is redundant for static constexprs.

#include &lt;array&gt;
#include &lt;iostream&gt;
class FastFtoa {
public:
static constexpr size_t N{39};
static constexpr std::array&lt;float, N&gt; _pos_pow_10 = []() {
std::array&lt;float, N&gt; x{1};
for (size_t i = 1; i &lt; N; i++)
x[i] = 10*x[i-1];
return x;
}();
static constexpr std::array&lt;float, N&gt; _inv_pow_10 = []() {
std::array&lt;float, N&gt; x{1};
for (size_t i = 1; i &lt; N; i++)
x[i] = .1f * x[i - 1];
return x;
}();
};
int main()
{
std::cout &lt;&lt; FastFtoa::_pos_pow_10[0] &lt;&lt; &#39;\n&#39;;
std::cout &lt;&lt; FastFtoa::_pos_pow_10[38] &lt;&lt; &#39;\n&#39;;
std::cout &lt;&lt; FastFtoa::_inv_pow_10[0] &lt;&lt; &#39;\n&#39;;
std::cout &lt;&lt; FastFtoa::_inv_pow_10[38] &lt;&lt; &#39;\n&#39;;
}

Output:

1
1e+38
1
1e-38

huangapple
  • 本文由 发表于 2023年6月19日 12:30:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76503612.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定