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?
source to share
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);
source to share
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.
source to share
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 .
source to share
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;
}
source to share