Generate a sequence of floats in a specific range

I want to fill with vector<float>

values ​​starting with a

, increasing by inc

, up to b

and including. So basically what, for example, vec = 2:0.5:4

in Matlab would do - vec

should now be { 2.0, 2.5, 3.0, 3.5, 4.0 }

.

The best I could think of is

vector<float> vec(10);
float increment = 0.5f;
std::generate(begin(vec), end(vec), [&increment]() {static float start = 2.0f; return start += increment ; });

      

But obviously this is not true as it starts at 2.5f and not 2.0f. And I would like to make the parameters a little easier or more concise.

I could imagine this in a custom class, but it takes quite some code to do it. Also I looked at std :: iota, but it can only grow by +1

.

Any ideas for a better, concise approach? Using C ++ 11 (and some of the 14 parts) is encouraged.

Edit: Of course I also used for-loop like:

    for (float i = -1.0f; i <= 1.0f; i += 0.05f) {
        vec.emplace_back(i);
    }

      

but it has a problem that it sometimes doesn't get to the final value like in this example, due to the inaccuracy of the float (or rather for the presentation). A fix that requires some code and I think there should be a more concise way?

+3


source to share


5 answers


You can write your own variation std::iota

, which also takes a stride argument.

template<typename ForwardIterator, typename T>
void strided_iota(ForwardIterator first, ForwardIterator last, T value, T stride)
{
    while(first != last) {
        *first++ = value;
        value += stride;
    }
}

      

In your example, you will use it like



std::vector<float> vec(10);
strided_iota(std::begin(vec), std::next(std::begin(vec), 5), 2.0f, 0.5f);

      

Live demo

+4


source


I don't think you really need any fancy features for this.

void fill_vec(vector<float>& vec, float a, float inc, float b)
{
    for(float n = a; n <= b; n += inc)
      vec.push_back(n);
}

      

If you're worried about floating point precision missing in the upper range, you can add a small amount (often denoted by epsilon for this sort of thing):



float eps = 0.0000001f;
for(float n = a; n <= b + eps; n += inc)

      

If enabled <cfloat>

, you can use FLT_EPSILON, which can vary between platforms as per implementation.

+2


source


If the problem is that you want to include all floating point values, then loop through the integers and do the necessary calculations to get back to the float values ​​in the loop.

for (int i = 20; i <= 40; i += 5) {
        vec.emplace_back(i/10.0);
    }

      

+1


source


You can use the functor that works for both iota

, as well as for generate

. Overload the function call and the increment operator appropriately:

template <typename T>
class ArithmeticProgression
{
    T val;
    T inc;

public:

    ArithmeticProg(T val, T inc) : val(val), inc(inc) {}

    ArithmeticProg& operator++() noexcept(noexcept(val += inc))
    {
        val += inc;
        return *this;
    }

    T operator()() noexcept(noexcept(val += inc))
    {
        auto tmp = val;
        val += inc;
        return tmp;
    }

    operator T() const noexcept {return val;}
};

template <typename T, typename U>
ArProg<typename std::common_type<T, U>::type> makeArithmeticProg( T val, U inc )
{
    return {val, inc};
}

      

Using:

int main()
{
    std::vector<float> vec;
    std::generate_n(std::back_inserter(vec), 5, makeArithmeticProg(2.0f, 0.5f) );

    for (auto f : vec)
        std::cout << f << ", ";

    std::cout << '\n';

    std::iota( std::begin(vec), std::end(vec), makeArithmeticProg(2.5f, 0.3f) );

    for (auto f : vec)
        std::cout << f << ", ";
}

      

Demo .

+1


source


Here's the approach:

#include <iostream>
#include <vector>
#include <algorithm>

// functor
class generator_float
{
    float _start, _inc;
public:
    generator_float(float start, float inc): _start(start), _inc(inc) {};
    float operator()() {
        float tmp = _start;
        _start += _inc;
        return tmp;
    }
};

int main()
{
    std::vector<float> vec(10);
    std::generate(std::begin(vec), std::end(vec), generator_float(2,0.5));
    for(auto&& elem: vec)
        std::cout << elem << " ";
    std::cout << std::endl;
}

      

+1


source







All Articles