Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何在编译时初始化浮点数组?_C++_Arrays_Templates_C++14_Constexpr - Fatal编程技术网

C++ 如何在编译时初始化浮点数组?

C++ 如何在编译时初始化浮点数组?,c++,arrays,templates,c++14,constexpr,C++,Arrays,Templates,C++14,Constexpr,我发现了两种在编译时和编译时初始化整数数组的好方法 不幸的是,两者都不能直接转换为初始化浮点数组;我发现我不够适合模板元编程,无法通过反复试验来解决这个问题 首先让我声明一个用例: constexpr unsigned int SineLength = 360u; constexpr unsigned int ArrayLength = SineLength+(SineLength/4u); constexpr double PI = 3.1415926535; float array[Ar

我发现了两种在编译时和编译时初始化整数数组的好方法

不幸的是,两者都不能直接转换为初始化浮点数组;我发现我不够适合模板元编程,无法通过反复试验来解决这个问题

首先让我声明一个用例:

constexpr unsigned int SineLength  = 360u;
constexpr unsigned int ArrayLength = SineLength+(SineLength/4u);
constexpr double PI = 3.1415926535;

float array[ArrayLength];

void fillArray(unsigned int length)
{
  for(unsigned int i = 0u; i < length; ++i)
    array[i] = sin(double(i)*PI/180.*360./double(SineLength));
}
这意味着需要
float
类型的模板参数。这是不允许的

现在,我想到的第一个想法是将浮点存储在一个int变量中——数组索引在计算后不会发生任何变化,因此假装它们是另一种类型(只要字节长度相等)是完全正确的

但请看:

constexpr int f(unsigned int i)
{
  float output = sin(double(i)*PI/180.*360./double(SineLength));
  return *(int*)&output;
}
不是有效的
constexpr
,因为它包含的语句多于return语句

constexpr int f(unsigned int i)
{
  return reinterpret_cast<int>(sin(double(i)*PI/180.*360./double(SineLength)));
}
与本质上相同的问题是:枚举不能是
float
类型,并且不能将该类型屏蔽为
int


现在,尽管我只是在“假装
浮点
整数
”的路径上解决了这个问题,但我实际上并不喜欢这条路径(除了它不起作用之外)。我更喜欢一种实际处理
float
的方法,就像处理
float
一样(也可以像处理
double
一样处理
double
),但我认为没有办法绕过强加的类型限制

遗憾的是,关于这个问题有很多问题,它们总是涉及积分类型,淹没了对这个专门问题的搜索。类似地,关于掩蔽一个类型的问题通常不考虑一个<代码> CONTXPRP或模板参数环境的限制。
请参阅和等。

假设您的实际目标是用一种简洁的方法初始化浮点数数组,它不一定拼写为
float array[N]
double array[N]
,而是
std::array
std::array
,这是可以做到的

数组类型的重要性在于可以复制
std::array
,这与
T[N]
不同。如果可以复制,则可以通过函数调用获取数组的内容,例如:

constexpr std::array<float, ArrayLength> array = fillArray<N>();
假设用于初始化数组元素的函数实际上是一个
constepr
表达式,这种方法可以生成
constepr
。函数
const_sin()
仅用于演示,但显然,它没有计算出sin(x)的合理近似值

这些评论表明,迄今为止的答案并不能完全解释发生了什么。那么,让我们把它分解成可消化的部分:

  • 目标是生成一个
    constexpr
    数组,该数组中填充了合适的值序列。但是,只需调整数组大小
    N
    ,就可以轻松更改数组大小。也就是说,从概念上讲,目标是创造

    constexpr float array[N] = { f(0), f(1), ..., f(N-1) };
    
    其中
    f()
    是生成
    constepr
    的合适函数。例如,
    f()
    可以定义为

    constexpr float f(int i) {
        return const_sin(double(i) * M_PI / 180.0 * 360.0 / double(Length);
    }
    
    但是,对
    f(0)
    f(1)
    等的调用需要随着
    N
    的每次更改而更改。因此,本质上与上述声明相同,但不需要额外键入

  • 解决方案的第一步是将
    float[N]
    替换为
    std:array
    :无法复制内置数组,而可以复制
    std::array
    。也就是说,可以将初始化委托给由
    N
    参数化的函数。也就是说,我们会使用

    template <std::size_t N>
    constexpr std::array<float, N> fillArray() {
        // some magic explained below goes here
    }
    constexpr std::array<float, N> array = fillArray<N>();
    
    因为扩展实际上等同于键入

    std::array<float, N>{ f(0), f(1), .., f(N-1) };
    
    std::数组{f(0),f(1),…,f(N-1)};
    
    所以问题变成了:如何获得这样一个参数包?我不认为它可以直接在函数中获得,但可以通过调用另一个具有合适参数的函数来获得

  • 使用别名
    std::make_index_序列
    是类型
    std::index_序列
    的别名。实现的细节有点神秘,但是
    std::make_index_sequence
    std::index_sequence
    ,friends是C++14的一部分(它们是基于,例如on提出的)。也就是说,我们所需要做的就是调用一个带有
    std::index_sequence
    类型参数的辅助函数,并从中获取参数包:

    template <std::size_t...I>
    constexpr std::array<float, sizeof...(I)>
    fillArray(std::index_sequence<I...>) {
        return std::array<float, sizeof...(I)>{ f(I)... };
    }
    template <std::size_t N>
    constexpr std::array<float, N> fillArray() {
        return fillArray(std::make_index_sequence<N>{});
    }
    
    模板
    constexpr std::数组
    fillArray(标准::索引\u序列){
    返回std::数组{f(I)…..};
    }
    模板
    constexpr std::数组fillArray(){
    返回fillArray(std::make_index_sequence{});
    }
    
    此函数的[unnamed]参数仅用于使参数pack
    std::size\u t。。。我可以推断


  • 我只是把这个答案留作文档。正如评论所说,我被gcc的宽容误导了。当使用
    f(42)
    作为模板参数时,它会失败,例如:

    constexpr float f(unsigned int i)
    {
      return sin(double(i)*PI/180.*360./double(SineLength));
    }
    
    constexpr double const_sin(double x) { return x * 3.1; } // dummy...
    template <std::size_t... I>
    constexpr std::array<float, sizeof...(I)> fillArray(std::index_sequence<I...>) {
        return std::array<float, sizeof...(I)>{
                const_sin(double(I)*M_PI/180.*360./double(SineLength))...
            };
    }
    
    template <std::size_t N>
    constexpr std::array<float, N> fillArray() {
        return fillArray(std::make_index_sequence<N>{});
    }
    
    std::array<int, f(42)> asdf;
    

    下面是一个生成sin值表的工作示例,通过传递不同的函数对象,您可以轻松地适应对数表

    #include <array>    // array
    #include <cmath>    // sin
    #include <cstddef>  // size_t
    #include <utility>  // index_sequence, make_index_sequence
    #include <iostream>
    
    namespace detail {
    
    template<class Function, std::size_t... Indices>
    constexpr auto make_array_helper(Function f, std::index_sequence<Indices...>)
            -> std::array<decltype(f(std::size_t{})), sizeof...(Indices)>
    {
            return {{ f(Indices)... }};
    }
    
    }       // namespace detail
    
    template<std::size_t N, class Function>
    constexpr auto make_array(Function f)
    {
            return detail::make_array_helper(f, std::make_index_sequence<N>{});
    }
    
    static auto const pi = std::acos(-1);
    static auto const make_sin = [](int x) { return std::sin(pi * x / 180.0); };
    static auto const sin_table = make_array<360>(make_sin);
    
    int main() 
    {
        for (auto elem : sin_table)
            std::cout << elem << "\n";
    }
    
    #包括

    请注意,您需要使用
    -stdlib=libc++
    ,因为
    libstdc++
    索引序列的实现效率非常低


    还要注意,您需要一个
    constepr
    函数对象(pi
    std::sin
    都不是
    constepr
    ),以便在编译时而不是在程序初始化时真正初始化数组

    如果要在编译时初始化浮点数组,有几个问题需要克服:

  • std::array
    有点破绽,因为在可变constexpr std::array的情况下,
    操作符[]
    不是constexpr(我相信这将在下一版本的标准中得到修复)

  • st
    std::array<int, f(42)> asdf;
    
    constexpr int floatAsInt(float float_val) {
      return *(int*)&float_val;
    }
    
    constexpr int f(unsigned int i) {
      return floatAsInt(sin(double(i)*PI/180.*360./double(SineLength)));
    }
    
    #include <array>    // array
    #include <cmath>    // sin
    #include <cstddef>  // size_t
    #include <utility>  // index_sequence, make_index_sequence
    #include <iostream>
    
    namespace detail {
    
    template<class Function, std::size_t... Indices>
    constexpr auto make_array_helper(Function f, std::index_sequence<Indices...>)
            -> std::array<decltype(f(std::size_t{})), sizeof...(Indices)>
    {
            return {{ f(Indices)... }};
    }
    
    }       // namespace detail
    
    template<std::size_t N, class Function>
    constexpr auto make_array(Function f)
    {
            return detail::make_array_helper(f, std::make_index_sequence<N>{});
    }
    
    static auto const pi = std::acos(-1);
    static auto const make_sin = [](int x) { return std::sin(pi * x / 180.0); };
    static auto const sin_table = make_array<360>(make_sin);
    
    int main() 
    {
        for (auto elem : sin_table)
            std::cout << elem << "\n";
    }
    
    #include <iostream>
    #include <cmath>
    #include <utility>
    #include <cassert>
    #include <string>
    #include <vector>
    
    namespace cpputil {
    
        // a fully constexpr version of array that allows incomplete
        // construction
        template<size_t N, class T>
        struct array
        {
            // public constructor defers to internal one for
            // conditional handling of missing arguments
            constexpr array(std::initializer_list<T> list)
            : array(list, std::make_index_sequence<N>())
            {
    
            }
    
            constexpr T& operator[](size_t i) noexcept {
                assert(i < N);
                return _data[i];
            }
    
            constexpr const T& operator[](size_t i) const noexcept {
                assert(i < N);
                return _data[i];
            }
    
            constexpr T& at(size_t i) noexcept {
                assert(i < N);
                return _data[i];
            }
    
            constexpr const T& at(size_t i) const noexcept {
                assert(i < N);
                return _data[i];
            }
    
            constexpr T* begin() {
                return std::addressof(_data[0]);
            }
    
            constexpr const T* begin() const {
                return std::addressof(_data[0]);
            }
    
            constexpr T* end() {
                // todo: maybe use std::addressof and disable compiler warnings
                // about array bounds that result
                return &_data[N];
            }
    
            constexpr const T* end() const {
                return &_data[N];
            }
    
            constexpr size_t size() const {
                return N;
            }
    
        private:
    
            T _data[N];
    
        private:
    
            // construct each element from the initialiser list if present
            // if not, default-construct
            template<size_t...Is>
            constexpr array(std::initializer_list<T> list, std::integer_sequence<size_t, Is...>)
            : _data {
                (
                 Is >= list.size()
                 ?
                 T()
                 :
                 std::move(*(std::next(list.begin(), Is)))
                 )...
            }
            {
    
            }
        };
    
        // convenience printer
        template<size_t N, class T>
        inline std::ostream& operator<<(std::ostream& os, const array<N, T>& a)
        {
            os << "[";
            auto sep = " ";
            for (const auto& i : a) {
                os << sep << i;
                sep = ", ";
            }
            return os << " ]";
        }
    
    }
    
    
    namespace trig
    {
        constexpr double pi() {
            return M_PI;
        }
    
        template<class T>
        auto constexpr to_radians(T degs) {
            return degs / 180.0 * pi();
        }
    
        // compile-time computation of a factorial
        constexpr double factorial(size_t x) {
            double result = 1.0;
            for (int i = 2 ; i <= x ; ++i)
                result *= double(i);
            return result;
        }
    
        // compile-time replacement for std::pow
        constexpr double power(double x, size_t n)
        {
            double result = 1;
            while (n--) {
                result *= x;
            }
            return result;
        }
    
        // compute a term in a taylor expansion at compile time
        constexpr double taylor_term(double x, size_t i)
        {
            int powers = 1 + (2 * i);
            double top = power(x, powers);
            double bottom = factorial(powers);
            auto term = top / bottom;
            if (i % 2 == 1)
                term = -term;
            return term;
        }
    
        // compute the sin(x) using `terms` terms in the taylor expansion        
        constexpr double taylor_expansion(double x, size_t terms)
        {
            auto result = x;
            for (int term = 1 ; term < terms ; ++term)
            {
                result += taylor_term(x, term);
            }
            return result;
        }
    
        // compute our interpolatable table as a constexpr
        template<size_t N = 1024>
        struct sin_table : cpputil::array<N, double>
        {
            static constexpr size_t cache_size = N;
            static constexpr double step_size = (pi() / 2) / cache_size;
            static constexpr double _360 = pi() * 2;
            static constexpr double _270 = pi() * 1.5;
            static constexpr double _180 = pi();
            static constexpr double _90 = pi() / 2;
    
            constexpr sin_table()
            : cpputil::array<N, double>({})
            {
                for(int slot = 0 ; slot < cache_size ; ++slot)
                {
                    double val = trig::taylor_expansion(step_size * slot, 20);
                    (*this)[slot] = val;
                }
            }
    
            double checked_interp_fw(double rads) const {
                size_t slot0 = size_t(rads / step_size);
                auto v0 = (slot0 >= this->size()) ? 1.0 : (*this)[slot0];
    
                size_t slot1 = slot0 + 1;
                auto v1 = (slot1 >= this->size()) ? 1.0 : (*this)[slot1];
    
                auto ratio = (rads - (slot0 * step_size)) / step_size;
    
                return (v1 * ratio) + (v0 * (1.0-ratio));
            }
    
            double interpolate(double rads) const
            {
                rads = std::fmod(rads, _360);
                if (rads < 0)
                    rads = std::fmod(_360 - rads, _360);
    
                if (rads < _90) {
                    return checked_interp_fw(rads);
                }
                else if (rads < _180) {
                    return checked_interp_fw(_90 - (rads - _90));
                }
                else if (rads < _270) {
                    return -checked_interp_fw(rads - _180);
                }
                else {
                    return -checked_interp_fw(_90 - (rads - _270));
                }
            }
    
    
        };
    
        double sine(double x)
        {
            if (x < 0) {
                return -sine(-x);
            }
            else {
                constexpr sin_table<> table;
                return table.interpolate(x);
            }
        }
    
    }
    
    
    void check(float degs) {
        using namespace std;
    
        cout << "checking : " << degs << endl;
        auto mysin = trig::sine(trig::to_radians(degs));
        auto stdsin = std::sin(trig::to_radians(degs));
        auto error = stdsin - mysin;
        cout << "mine=" << mysin << ", std=" << stdsin << ", error=" << error << endl;
        cout << endl;
    }
    
    auto main() -> int
    {
    
        check(0.5);
        check(30);
        check(45.4);
        check(90);
        check(151);
        check(180);
        check(195);
        check(89.5);
        check(91);
        check(270);
        check(305);
        check(360);
        return 0;
    }
    
    checking : 0.5
    mine=0.00872653, std=0.00872654, error=2.15177e-09
    
    checking : 30
    mine=0.5, std=0.5, error=1.30766e-07
    
    checking : 45.4
    mine=0.712026, std=0.712026, error=2.07233e-07
    
    checking : 90
    mine=1, std=1, error=0
    
    checking : 151
    mine=0.48481, std=0.48481, error=2.42041e-08
    
    checking : 180
    mine=-0, std=1.22465e-16, error=1.22465e-16
    
    checking : 195
    mine=-0.258819, std=-0.258819, error=-6.76265e-08
    
    checking : 89.5
    mine=0.999962, std=0.999962, error=2.5215e-07
    
    checking : 91
    mine=0.999847, std=0.999848, error=2.76519e-07
    
    checking : 270
    mine=-1, std=-1, error=0
    
    checking : 305
    mine=-0.819152, std=-0.819152, error=-1.66545e-07
    
    checking : 360
    mine=0, std=-2.44929e-16, error=-2.44929e-16