Aleph0
Aleph0

Reputation: 6084

Easiest way to fill std::vector<double> with equidistant values

Assume that I have the values double start, double end and a stepsize double step.

What is the easiest way to fill std::vector<double> with values beginning at start and incrementing by stepsize as long as the current value is less than end?

I'm asking myself, if there is an stl function making this task a one-liner.

std::vector<double> fill(double start, double end, double step) {
  // Code
}

main() {
  auto ret=fill(0.2, 2.3, 0.2);
  // ret = {0.2, 0.4, 0.6, ... , 2.2}
}

Upvotes: 5

Views: 2651

Answers (3)

Richard Hodges
Richard Hodges

Reputation: 69882

again out of academic interest, and probably bending the intended design of std::iota to breaking point:

std::iota(x.begin(), x.end(), double_iota(step, min));

With the following definition of double_iota:

struct double_iota
{
    double_iota(double inc, double init_value = 0.0) : _value(init_value), _inc(inc) {}

    operator double() const { return _value; }
    double_iota& operator++() { _value += _inc; return *this; }
    double _value;
    double _inc;
};

Test program:

#include <algorithm>
#include <numeric>
#include <vector>
#include <iostream>
#include <iterator>

struct double_iota
{
    double_iota(double inc, double init_value = 0.0) : _value(init_value), _inc(inc) {}

    operator double() const { return _value; }
    double_iota& operator++() { _value += _inc; return *this; }
    double _value;
    double _inc;
};

int main()
{
    double min = 1.0;
    double max = 2.3;
    double step = 0.2;

    std::vector<double> x(std::size_t(((max + step - std::numeric_limits<double>::epsilon()) - min) / step));
    std::iota(x.begin(), x.end(), double_iota(step, min));

    std::copy(x.begin(), x.end(), std::ostream_iterator<double>(std::cout, ", "));
}

expected results:

1, 1.2, 1.4, 1.6, 1.8, 2, 2.2, 

update:

or we can build a custom iterator which allows us to express the sequence truly in one line:

    std::vector<double> x(double_inc_iterator(min, step), double_inc_iterator(max));

as follows:

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


struct double_inc_iterator : std::iterator<std::forward_iterator_tag, double>
{
    double_inc_iterator(double initial, double inc = 1.0) : _value(initial), _inc(inc) {}
    value_type operator*() const { return _value; }
    double_inc_iterator& operator++() { _value += _inc; return *this; }

    bool operator==(double_inc_iterator const& r) const { return _value >= r._value; }
    bool operator!=(double_inc_iterator const& r) const { return !(*this == r); }

    value_type _value;
    value_type _inc;
};

int main()
{
    double min = 1.0;
    double max = 2.3;
    double step = 0.2;

    std::vector<double> x(double_inc_iterator(min, step), double_inc_iterator(max));

    std::copy(x.begin(), x.end(), std::ostream_iterator<double>(std::cout, ", "));
}

Now we don't even need the intermediate vector:

std::copy(double_inc_iterator(min, step),
          double_inc_iterator(max),
          std::ostream_iterator<double>(std::cout, ", "));

Upvotes: 5

Daniel
Daniel

Reputation: 8441

This can be done with std::partial_sum:

#include <vector>
#include <numeric>
#include <iterator>
#include <limits>
#include <cstddef>
#include <stdexcept>
#include <iostream>

namespace detail {
    template <typename T, typename S>
    constexpr bool is_end_reachable(const T start, const T end, const S step) noexcept
    {
        return (end > start && step > 0) || start == end || (end < start && step < 0);
    }

    template <typename T, typename S>
    constexpr std::size_t num_values(const T start, const T end, const S step) noexcept
    {
        // Add one as start is always included. Add epsilon to avoid floating point errors.
        return (((end - start) + std::numeric_limits<T>::epsilon()) / step) + 1;
    }
} // namespace detail

template <typename T, typename S>
std::vector<T> range(const T start, const T end, const S step = 1)
{
    if (!detail::is_end_reachable(start, end, step)) {
        throw std::invalid_argument {"end not reachable from start"};
    }
    std::vector<T> result(detail::num_values(start, end, step), step);
    result.front() = start;
    std::partial_sum(std::cbegin(result), std::cend(result), std::begin(result));
    return result;
}

A few extra casts are needed for full type robustness but the idea is the same.

Example:

int main()
{
    const auto v = range(-0.1, -2.7, -0.3312);
    std::copy(std::cbegin(v), std::cend(v), std::ostream_iterator<double> {std::cout, " "});
    std::cout << std::endl;
}

Live On Coliru

Upvotes: 0

Smeeheey
Smeeheey

Reputation: 10316

Just for academic purposes, you could:

std::vector<double> result;
std::generate_n(std::back_inserter(result), (size_t)((end-start)/step), [&start, step](){ auto ret=start; start+=step; return ret; });

Upvotes: 2

Related Questions