现代C++如何创建constexpr数组的幂10?

zrfyljdw  于 2023-06-25  发布在  其他
关注(0)|答案(3)|浏览(220)
#include <iostream>
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 << FastFtoa::__inv_pow_10[37] << " " << FastFtoa::__pos_pow_10[37] << "\n";
    return 0;
}

我需要创建10^x10^-xconstexpr数组,x范围-64..64 -38..38。它用于快速浮点到字符串转换。它还需要在一个静态类中(所有函数都是public static)。
可以像上面的代码一样手动创建它们,但看起来不太好。更不用说完整的float64范围是10^308,如果手动完成,这将是一个非常丑陋的代码。
在C++17/20中,什么是正确的方法?

1l5u6lss

1l5u6lss1#

#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;
}
cigdeys3

cigdeys32#

可以使用未命名的lambda初始化constexpr std::array,以减少命名空间混乱。inline对于静态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';
}

输出:

1
1e+38
1
1e-38
wljmcqd8

wljmcqd83#

你可以利用std::array是constexpr的事实。只要您有可以在编译时完成的计算,您就可以用计算值填充该数组。
在该示例中:make_pow_10_map生成10的幂的Map。简单地在一个循环中将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;
}

相关问题