How to create a `range`-like iterable object of floats?How do I iterate over the words of a string?How do I check if a string is a number (float)?How do I parse a string to a float or int in Python?How to use a decimal range() step value?How to format a float in javascript?How can I force division to be floating point? Division keeps rounding down to 0?How to deal with floating point number precision in JavaScript?How to iterate through two lists in parallel?How to get a random number between a float range?How to convert an iterator to a stream?
Building a road to escape Earth's gravity by making a pyramid on Antartica
Why is the relationship between frequency and pitch exponential?
Company did not petition for visa in a timely manner. Is asking me to work from overseas, but wants me to take a paycut
How bad would a partial hash leak be, realistically?
Why doesn’t a normal window produce an apparent rainbow?
Movie about a boy who was born old and grew young
Payment instructions from HomeAway look fishy to me
Strange symbol for two functions
Do any instruments not produce overtones?
Does there exist a word to express a male who behaves as a female?
Can a user sell my software (MIT license) without modification?
What can plausibly explain many of my very long and low-tech bridges?
What risks are there when you clear your cookies instead of logging off?
Turing patterns
How to translate “Me doing X” like in online posts?
Traffic law UK, pedestrians
How to generate random points without duplication?
When writing an error prompt, should we end the sentence with a exclamation mark or a dot?
Phone number to a lounge, or lounges generally
What is this solid state starting relay component?
Trapping Rain Water
Does the growth of home value benefit from compound interest?
2.8 is missing the Carve option in the Boolean Modifier
Version 2 - print new even-length arrays from two arrays
How to create a `range`-like iterable object of floats?
How do I iterate over the words of a string?How do I check if a string is a number (float)?How do I parse a string to a float or int in Python?How to use a decimal range() step value?How to format a float in javascript?How can I force division to be floating point? Division keeps rounding down to 0?How to deal with floating point number precision in JavaScript?How to iterate through two lists in parallel?How to get a random number between a float range?How to convert an iterator to a stream?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I want to create a range
-like construct in c++, that will be used like this:
for (auto i: range(5,9))
cout << i << ' '; // prints 5 6 7 8
for (auto i: range(5.1,9.2))
cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
Handling the integer case is relatively easy:
template<typename T>
struct range
T from, to;
range(T from, T to) : from(from), to(to)
struct iterator
T current;
T operator*() return current;
iterator& operator++()
++current;
return *this;
bool operator==(const iterator& other) return current == other.current;
bool operator!=(const iterator& other) return current != other.current;
;
iterator begin() const return iterator from ;
iterator end() const return iterator to ;
;
However, this does not work in the float
case, since the standard range-based loop in C++
checks whether iter==end
, and not whether iter <= end
as you would do in a for a loop.
Is there a simple way to create an iterable object that will behave like a correct for loop on float
s?
c++ floating-point iterator range
|
show 3 more comments
I want to create a range
-like construct in c++, that will be used like this:
for (auto i: range(5,9))
cout << i << ' '; // prints 5 6 7 8
for (auto i: range(5.1,9.2))
cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
Handling the integer case is relatively easy:
template<typename T>
struct range
T from, to;
range(T from, T to) : from(from), to(to)
struct iterator
T current;
T operator*() return current;
iterator& operator++()
++current;
return *this;
bool operator==(const iterator& other) return current == other.current;
bool operator!=(const iterator& other) return current != other.current;
;
iterator begin() const return iterator from ;
iterator end() const return iterator to ;
;
However, this does not work in the float
case, since the standard range-based loop in C++
checks whether iter==end
, and not whether iter <= end
as you would do in a for a loop.
Is there a simple way to create an iterable object that will behave like a correct for loop on float
s?
c++ floating-point iterator range
Maybe a specialization ofoperator==
for floating-point types that subverts the semantics by usingcurrent<=other.current
?
– Some programmer dude
May 20 at 9:06
8
What about implementing a specialend
iterator, which would be set inoperator++()
when the incremented value exceedsto
?
– Daniel Langr
May 20 at 9:12
Since coroutines have been mentioned, why not use the upcoming ranges library? (Or the range library that was the base for the standard?)
– Some programmer dude
May 20 at 9:21
8
You should be aware that floating-point rounding will affect your loop. For example, with the IEEE-754 format commonly used fordouble
,for (double x = 1.03; x <= 11.03; x += 1)
will end whenx
is about 10.03, not 11.03. It will be incremented to 11.030000000000001136868377216160297393798828125, but11.03
in source code becomes the value 11.0299999999999993605115378159098327159881591796875, sox <= 11.03
evaluates to false.
– Eric Postpischil
May 20 at 17:41
2
It's a lot safer to use a linspace-style explicit count of elements (and no default count, unlike MATLAB or numpy linspace), rather than starting from a step value and deriving the number of elements from there. A count-oriented instead of step-size-oriented approach eliminates issues with unexpectedly including or excluding the endpoint.
– user2357112
May 20 at 21:41
|
show 3 more comments
I want to create a range
-like construct in c++, that will be used like this:
for (auto i: range(5,9))
cout << i << ' '; // prints 5 6 7 8
for (auto i: range(5.1,9.2))
cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
Handling the integer case is relatively easy:
template<typename T>
struct range
T from, to;
range(T from, T to) : from(from), to(to)
struct iterator
T current;
T operator*() return current;
iterator& operator++()
++current;
return *this;
bool operator==(const iterator& other) return current == other.current;
bool operator!=(const iterator& other) return current != other.current;
;
iterator begin() const return iterator from ;
iterator end() const return iterator to ;
;
However, this does not work in the float
case, since the standard range-based loop in C++
checks whether iter==end
, and not whether iter <= end
as you would do in a for a loop.
Is there a simple way to create an iterable object that will behave like a correct for loop on float
s?
c++ floating-point iterator range
I want to create a range
-like construct in c++, that will be used like this:
for (auto i: range(5,9))
cout << i << ' '; // prints 5 6 7 8
for (auto i: range(5.1,9.2))
cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
Handling the integer case is relatively easy:
template<typename T>
struct range
T from, to;
range(T from, T to) : from(from), to(to)
struct iterator
T current;
T operator*() return current;
iterator& operator++()
++current;
return *this;
bool operator==(const iterator& other) return current == other.current;
bool operator!=(const iterator& other) return current != other.current;
;
iterator begin() const return iterator from ;
iterator end() const return iterator to ;
;
However, this does not work in the float
case, since the standard range-based loop in C++
checks whether iter==end
, and not whether iter <= end
as you would do in a for a loop.
Is there a simple way to create an iterable object that will behave like a correct for loop on float
s?
c++ floating-point iterator range
c++ floating-point iterator range
edited May 20 at 19:06
JeJo
5,75331028
5,75331028
asked May 20 at 9:01
Erel Segal-HaleviErel Segal-Halevi
12.8k2371121
12.8k2371121
Maybe a specialization ofoperator==
for floating-point types that subverts the semantics by usingcurrent<=other.current
?
– Some programmer dude
May 20 at 9:06
8
What about implementing a specialend
iterator, which would be set inoperator++()
when the incremented value exceedsto
?
– Daniel Langr
May 20 at 9:12
Since coroutines have been mentioned, why not use the upcoming ranges library? (Or the range library that was the base for the standard?)
– Some programmer dude
May 20 at 9:21
8
You should be aware that floating-point rounding will affect your loop. For example, with the IEEE-754 format commonly used fordouble
,for (double x = 1.03; x <= 11.03; x += 1)
will end whenx
is about 10.03, not 11.03. It will be incremented to 11.030000000000001136868377216160297393798828125, but11.03
in source code becomes the value 11.0299999999999993605115378159098327159881591796875, sox <= 11.03
evaluates to false.
– Eric Postpischil
May 20 at 17:41
2
It's a lot safer to use a linspace-style explicit count of elements (and no default count, unlike MATLAB or numpy linspace), rather than starting from a step value and deriving the number of elements from there. A count-oriented instead of step-size-oriented approach eliminates issues with unexpectedly including or excluding the endpoint.
– user2357112
May 20 at 21:41
|
show 3 more comments
Maybe a specialization ofoperator==
for floating-point types that subverts the semantics by usingcurrent<=other.current
?
– Some programmer dude
May 20 at 9:06
8
What about implementing a specialend
iterator, which would be set inoperator++()
when the incremented value exceedsto
?
– Daniel Langr
May 20 at 9:12
Since coroutines have been mentioned, why not use the upcoming ranges library? (Or the range library that was the base for the standard?)
– Some programmer dude
May 20 at 9:21
8
You should be aware that floating-point rounding will affect your loop. For example, with the IEEE-754 format commonly used fordouble
,for (double x = 1.03; x <= 11.03; x += 1)
will end whenx
is about 10.03, not 11.03. It will be incremented to 11.030000000000001136868377216160297393798828125, but11.03
in source code becomes the value 11.0299999999999993605115378159098327159881591796875, sox <= 11.03
evaluates to false.
– Eric Postpischil
May 20 at 17:41
2
It's a lot safer to use a linspace-style explicit count of elements (and no default count, unlike MATLAB or numpy linspace), rather than starting from a step value and deriving the number of elements from there. A count-oriented instead of step-size-oriented approach eliminates issues with unexpectedly including or excluding the endpoint.
– user2357112
May 20 at 21:41
Maybe a specialization of
operator==
for floating-point types that subverts the semantics by using current<=other.current
?– Some programmer dude
May 20 at 9:06
Maybe a specialization of
operator==
for floating-point types that subverts the semantics by using current<=other.current
?– Some programmer dude
May 20 at 9:06
8
8
What about implementing a special
end
iterator, which would be set in operator++()
when the incremented value exceeds to
?– Daniel Langr
May 20 at 9:12
What about implementing a special
end
iterator, which would be set in operator++()
when the incremented value exceeds to
?– Daniel Langr
May 20 at 9:12
Since coroutines have been mentioned, why not use the upcoming ranges library? (Or the range library that was the base for the standard?)
– Some programmer dude
May 20 at 9:21
Since coroutines have been mentioned, why not use the upcoming ranges library? (Or the range library that was the base for the standard?)
– Some programmer dude
May 20 at 9:21
8
8
You should be aware that floating-point rounding will affect your loop. For example, with the IEEE-754 format commonly used for
double
, for (double x = 1.03; x <= 11.03; x += 1)
will end when x
is about 10.03, not 11.03. It will be incremented to 11.030000000000001136868377216160297393798828125, but 11.03
in source code becomes the value 11.0299999999999993605115378159098327159881591796875, so x <= 11.03
evaluates to false.– Eric Postpischil
May 20 at 17:41
You should be aware that floating-point rounding will affect your loop. For example, with the IEEE-754 format commonly used for
double
, for (double x = 1.03; x <= 11.03; x += 1)
will end when x
is about 10.03, not 11.03. It will be incremented to 11.030000000000001136868377216160297393798828125, but 11.03
in source code becomes the value 11.0299999999999993605115378159098327159881591796875, so x <= 11.03
evaluates to false.– Eric Postpischil
May 20 at 17:41
2
2
It's a lot safer to use a linspace-style explicit count of elements (and no default count, unlike MATLAB or numpy linspace), rather than starting from a step value and deriving the number of elements from there. A count-oriented instead of step-size-oriented approach eliminates issues with unexpectedly including or excluding the endpoint.
– user2357112
May 20 at 21:41
It's a lot safer to use a linspace-style explicit count of elements (and no default count, unlike MATLAB or numpy linspace), rather than starting from a step value and deriving the number of elements from there. A count-oriented instead of step-size-oriented approach eliminates issues with unexpectedly including or excluding the endpoint.
– user2357112
May 20 at 21:41
|
show 3 more comments
4 Answers
4
active
oldest
votes
Here is my attempt, which does not hamper the semantics of iterators. The change is that now, each iterator knows its stopping value, to which it will set itself upon exceeding it. All end iterators of a range with equal to
therefore compare equal.
template <typename T>
struct range
T from, to;
range(T from, T to): from(from), to(to)
struct iterator
const T to; // iterator knows its bounds
T current;
T operator*() return current;
iterator& operator++()
++current;
if(current > to)
// make it an end iterator
// (current being exactly equal to 'current' of other end iterators)
current = to;
return *this;
bool operator==(const iterator& other) const // OT: note the const
return current == other.current;
// OT: this is how we do !=
bool operator!=(const iterator& other) const return !(*this == other);
;
iterator begin() const return iteratorto, from;
iterator end() const return iteratorto, to;
;
Why is this better?
The solution by @JeJo relies on the order in which you compare those iterators, i.e. it != end
or end != it
. But, in the case of range-based for, it is defined. Should you use this contraption in some other context, I advise the above approach.
Alternatively, if sizeof(T) > sizeof(void*)
, it makes sense to store a pointer to the originating range
instance (which in the case of the range-for persists until the end) and use that to refer to a single T
value:
template <typename T>
struct range
T from, to;
range(T from, T to): from(from), to(to)
struct iterator
const range* range;
T current;
iterator& operator++()
++current;
if(current > range->to)
current = range->to;
return *this;
...
;
iterator begin() const return iteratorthis, from;
iterator end() const return iteratorthis, to;
;
Or it could be T const* const
pointing directly to that value, it is up to you.
OT: Do not forget to make the internals private
for both classes.
add a comment |
Instead of a range object you could use a generator (a coroutine using co_yield
). Despite it is not in the standard (but planned for C++20), some compilers already implement it.
See: https://en.cppreference.com/w/cpp/language/coroutines
With MSVC it would be:
#include <iostream>
#include <experimental/generator>
std::experimental::generator<double> rangeGenerator(double from, double to)
for (double x=from;x <= to;x++)
co_yield x;
int main()
for (auto i : rangeGenerator(5.1, 9.2))
std::cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
add a comment |
Is there a simple way to create an iterable object that will behave
like a correct for loop onfloat
s?
The simplest hack† would be using the traits std::is_floating_point
to provide different return (i.e. iter <= end
) within the operator!=
overload.
(See Live)
#include <type_traits>
bool operator!=(const iterator& other)
if constexpr (std::is_floating_point_v<T>) return current <= other.current;
return !(*this == other);
†Warning: Even though that does the job, it breaks the meaning of operator!=
overload.
Alternative Solution
The entire range
class can be replaced by a simple function in which the values of the range will be populated with the help of std::iota
in the standard container std::vector
.
Use SFINE, to restrict the use of the function for only the valid types.
This way, you can rely on standard implementations and forget about the reinventions.
(See Live)
#include <iostream>
#include <type_traits>
#include <vector> // std::vector
#include <numeric> // std::iota
#include <cstddef> // std::size_t
#include <cmath> // std::modf
// traits for valid template types(integers and floating points)
template<typename Type>
using is_integers_and_floats = std::conjunction<
std::is_arithmetic<Type>,
std::negation<std::is_same<Type, bool>>,
std::negation<std::is_same<Type, char>>,
std::negation<std::is_same<Type, char16_t>>,
std::negation<std::is_same<Type, char32_t>>,
std::negation<std::is_same<Type, wchar_t>>
/*, std::negation<std::is_same<char8_t, Type>> */ // since C++20
>;
template <typename T>
auto ragesof(const T begin, const T end)
-> std::enable_if_t<is_integers_and_floats<T>::value, std::vector<T>>
if (begin >= end) return std::vector<T>; // edge case to be considered
// find the number of elements between the range
const std::size_t size = [begin, end]() -> std::size_t
const std::size_t diffWhole
= static_cast<std::size_t>(end) - static_cast<std::size_t>(begin);
if constexpr (std::is_floating_point_v<T>)
double whole; // get the decimal parts of begin and end
const double decimalBegin = std::modf(static_cast<double>(begin), &whole);
const double decimalEnd = std::modf(static_cast<double>(end), &whole);
return decimalBegin <= decimalEnd ? diffWhole + 1 : diffWhole;
return diffWhole;
();
// construct and initialize the `std::vector` with size
std::vector<T> vec(size);
// populates the range from [first, end)
std::iota(std::begin(vec), std::end(vec), begin);
return vec;
int main()
for (auto i : ragesof( 5, 9 ))
std::cout << i << ' '; // prints 5 6 7 8
std::cout << 'n';
for (auto i : ragesof(5.1, 9.2))
std::cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
add a comment |
A floating-point loop or iterator should typically use integer types to hold the total number of iterations and the number of the current iteration, and then compute the "loop index" value used within the loop based upon those and loop-invariant floating-point values.
For example:
for (int i=-10; i<=10; i++)
double x = i/10.0; // Substituting i*0.1 would be faster but less accurate
or
for (int i=0; i<=16; i++)
double x = ((startValue*(16-i))+(endValue*i))*(1/16);
Note that there is no possibility of rounding errors affecting the number of iterations. The latter calculation is guaranteed to yield a correctly-rounded result at the endpoints; computing startValue+i*(endValue-startValue)
would likely be faster (since the loop-invariant (endValue-startValue)
can be hoisted) but may be less accurate.
Using an integer iterator along with a function to convert an integer to a floating-point value is probably the most robust way to iterate over a range of floating-point values. Trying to iterate over floating-point values directly is far more likely to yield "off-by-one" errors.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56217541%2fhow-to-create-a-range-like-iterable-object-of-floats%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
Here is my attempt, which does not hamper the semantics of iterators. The change is that now, each iterator knows its stopping value, to which it will set itself upon exceeding it. All end iterators of a range with equal to
therefore compare equal.
template <typename T>
struct range
T from, to;
range(T from, T to): from(from), to(to)
struct iterator
const T to; // iterator knows its bounds
T current;
T operator*() return current;
iterator& operator++()
++current;
if(current > to)
// make it an end iterator
// (current being exactly equal to 'current' of other end iterators)
current = to;
return *this;
bool operator==(const iterator& other) const // OT: note the const
return current == other.current;
// OT: this is how we do !=
bool operator!=(const iterator& other) const return !(*this == other);
;
iterator begin() const return iteratorto, from;
iterator end() const return iteratorto, to;
;
Why is this better?
The solution by @JeJo relies on the order in which you compare those iterators, i.e. it != end
or end != it
. But, in the case of range-based for, it is defined. Should you use this contraption in some other context, I advise the above approach.
Alternatively, if sizeof(T) > sizeof(void*)
, it makes sense to store a pointer to the originating range
instance (which in the case of the range-for persists until the end) and use that to refer to a single T
value:
template <typename T>
struct range
T from, to;
range(T from, T to): from(from), to(to)
struct iterator
const range* range;
T current;
iterator& operator++()
++current;
if(current > range->to)
current = range->to;
return *this;
...
;
iterator begin() const return iteratorthis, from;
iterator end() const return iteratorthis, to;
;
Or it could be T const* const
pointing directly to that value, it is up to you.
OT: Do not forget to make the internals private
for both classes.
add a comment |
Here is my attempt, which does not hamper the semantics of iterators. The change is that now, each iterator knows its stopping value, to which it will set itself upon exceeding it. All end iterators of a range with equal to
therefore compare equal.
template <typename T>
struct range
T from, to;
range(T from, T to): from(from), to(to)
struct iterator
const T to; // iterator knows its bounds
T current;
T operator*() return current;
iterator& operator++()
++current;
if(current > to)
// make it an end iterator
// (current being exactly equal to 'current' of other end iterators)
current = to;
return *this;
bool operator==(const iterator& other) const // OT: note the const
return current == other.current;
// OT: this is how we do !=
bool operator!=(const iterator& other) const return !(*this == other);
;
iterator begin() const return iteratorto, from;
iterator end() const return iteratorto, to;
;
Why is this better?
The solution by @JeJo relies on the order in which you compare those iterators, i.e. it != end
or end != it
. But, in the case of range-based for, it is defined. Should you use this contraption in some other context, I advise the above approach.
Alternatively, if sizeof(T) > sizeof(void*)
, it makes sense to store a pointer to the originating range
instance (which in the case of the range-for persists until the end) and use that to refer to a single T
value:
template <typename T>
struct range
T from, to;
range(T from, T to): from(from), to(to)
struct iterator
const range* range;
T current;
iterator& operator++()
++current;
if(current > range->to)
current = range->to;
return *this;
...
;
iterator begin() const return iteratorthis, from;
iterator end() const return iteratorthis, to;
;
Or it could be T const* const
pointing directly to that value, it is up to you.
OT: Do not forget to make the internals private
for both classes.
add a comment |
Here is my attempt, which does not hamper the semantics of iterators. The change is that now, each iterator knows its stopping value, to which it will set itself upon exceeding it. All end iterators of a range with equal to
therefore compare equal.
template <typename T>
struct range
T from, to;
range(T from, T to): from(from), to(to)
struct iterator
const T to; // iterator knows its bounds
T current;
T operator*() return current;
iterator& operator++()
++current;
if(current > to)
// make it an end iterator
// (current being exactly equal to 'current' of other end iterators)
current = to;
return *this;
bool operator==(const iterator& other) const // OT: note the const
return current == other.current;
// OT: this is how we do !=
bool operator!=(const iterator& other) const return !(*this == other);
;
iterator begin() const return iteratorto, from;
iterator end() const return iteratorto, to;
;
Why is this better?
The solution by @JeJo relies on the order in which you compare those iterators, i.e. it != end
or end != it
. But, in the case of range-based for, it is defined. Should you use this contraption in some other context, I advise the above approach.
Alternatively, if sizeof(T) > sizeof(void*)
, it makes sense to store a pointer to the originating range
instance (which in the case of the range-for persists until the end) and use that to refer to a single T
value:
template <typename T>
struct range
T from, to;
range(T from, T to): from(from), to(to)
struct iterator
const range* range;
T current;
iterator& operator++()
++current;
if(current > range->to)
current = range->to;
return *this;
...
;
iterator begin() const return iteratorthis, from;
iterator end() const return iteratorthis, to;
;
Or it could be T const* const
pointing directly to that value, it is up to you.
OT: Do not forget to make the internals private
for both classes.
Here is my attempt, which does not hamper the semantics of iterators. The change is that now, each iterator knows its stopping value, to which it will set itself upon exceeding it. All end iterators of a range with equal to
therefore compare equal.
template <typename T>
struct range
T from, to;
range(T from, T to): from(from), to(to)
struct iterator
const T to; // iterator knows its bounds
T current;
T operator*() return current;
iterator& operator++()
++current;
if(current > to)
// make it an end iterator
// (current being exactly equal to 'current' of other end iterators)
current = to;
return *this;
bool operator==(const iterator& other) const // OT: note the const
return current == other.current;
// OT: this is how we do !=
bool operator!=(const iterator& other) const return !(*this == other);
;
iterator begin() const return iteratorto, from;
iterator end() const return iteratorto, to;
;
Why is this better?
The solution by @JeJo relies on the order in which you compare those iterators, i.e. it != end
or end != it
. But, in the case of range-based for, it is defined. Should you use this contraption in some other context, I advise the above approach.
Alternatively, if sizeof(T) > sizeof(void*)
, it makes sense to store a pointer to the originating range
instance (which in the case of the range-for persists until the end) and use that to refer to a single T
value:
template <typename T>
struct range
T from, to;
range(T from, T to): from(from), to(to)
struct iterator
const range* range;
T current;
iterator& operator++()
++current;
if(current > range->to)
current = range->to;
return *this;
...
;
iterator begin() const return iteratorthis, from;
iterator end() const return iteratorthis, to;
;
Or it could be T const* const
pointing directly to that value, it is up to you.
OT: Do not forget to make the internals private
for both classes.
edited May 21 at 7:20
answered May 20 at 9:19
LogicStuffLogicStuff
16.7k64061
16.7k64061
add a comment |
add a comment |
Instead of a range object you could use a generator (a coroutine using co_yield
). Despite it is not in the standard (but planned for C++20), some compilers already implement it.
See: https://en.cppreference.com/w/cpp/language/coroutines
With MSVC it would be:
#include <iostream>
#include <experimental/generator>
std::experimental::generator<double> rangeGenerator(double from, double to)
for (double x=from;x <= to;x++)
co_yield x;
int main()
for (auto i : rangeGenerator(5.1, 9.2))
std::cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
add a comment |
Instead of a range object you could use a generator (a coroutine using co_yield
). Despite it is not in the standard (but planned for C++20), some compilers already implement it.
See: https://en.cppreference.com/w/cpp/language/coroutines
With MSVC it would be:
#include <iostream>
#include <experimental/generator>
std::experimental::generator<double> rangeGenerator(double from, double to)
for (double x=from;x <= to;x++)
co_yield x;
int main()
for (auto i : rangeGenerator(5.1, 9.2))
std::cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
add a comment |
Instead of a range object you could use a generator (a coroutine using co_yield
). Despite it is not in the standard (but planned for C++20), some compilers already implement it.
See: https://en.cppreference.com/w/cpp/language/coroutines
With MSVC it would be:
#include <iostream>
#include <experimental/generator>
std::experimental::generator<double> rangeGenerator(double from, double to)
for (double x=from;x <= to;x++)
co_yield x;
int main()
for (auto i : rangeGenerator(5.1, 9.2))
std::cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
Instead of a range object you could use a generator (a coroutine using co_yield
). Despite it is not in the standard (but planned for C++20), some compilers already implement it.
See: https://en.cppreference.com/w/cpp/language/coroutines
With MSVC it would be:
#include <iostream>
#include <experimental/generator>
std::experimental::generator<double> rangeGenerator(double from, double to)
for (double x=from;x <= to;x++)
co_yield x;
int main()
for (auto i : rangeGenerator(5.1, 9.2))
std::cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
edited May 20 at 9:46
answered May 20 at 9:15
P. PICARDP. PICARD
1869
1869
add a comment |
add a comment |
Is there a simple way to create an iterable object that will behave
like a correct for loop onfloat
s?
The simplest hack† would be using the traits std::is_floating_point
to provide different return (i.e. iter <= end
) within the operator!=
overload.
(See Live)
#include <type_traits>
bool operator!=(const iterator& other)
if constexpr (std::is_floating_point_v<T>) return current <= other.current;
return !(*this == other);
†Warning: Even though that does the job, it breaks the meaning of operator!=
overload.
Alternative Solution
The entire range
class can be replaced by a simple function in which the values of the range will be populated with the help of std::iota
in the standard container std::vector
.
Use SFINE, to restrict the use of the function for only the valid types.
This way, you can rely on standard implementations and forget about the reinventions.
(See Live)
#include <iostream>
#include <type_traits>
#include <vector> // std::vector
#include <numeric> // std::iota
#include <cstddef> // std::size_t
#include <cmath> // std::modf
// traits for valid template types(integers and floating points)
template<typename Type>
using is_integers_and_floats = std::conjunction<
std::is_arithmetic<Type>,
std::negation<std::is_same<Type, bool>>,
std::negation<std::is_same<Type, char>>,
std::negation<std::is_same<Type, char16_t>>,
std::negation<std::is_same<Type, char32_t>>,
std::negation<std::is_same<Type, wchar_t>>
/*, std::negation<std::is_same<char8_t, Type>> */ // since C++20
>;
template <typename T>
auto ragesof(const T begin, const T end)
-> std::enable_if_t<is_integers_and_floats<T>::value, std::vector<T>>
if (begin >= end) return std::vector<T>; // edge case to be considered
// find the number of elements between the range
const std::size_t size = [begin, end]() -> std::size_t
const std::size_t diffWhole
= static_cast<std::size_t>(end) - static_cast<std::size_t>(begin);
if constexpr (std::is_floating_point_v<T>)
double whole; // get the decimal parts of begin and end
const double decimalBegin = std::modf(static_cast<double>(begin), &whole);
const double decimalEnd = std::modf(static_cast<double>(end), &whole);
return decimalBegin <= decimalEnd ? diffWhole + 1 : diffWhole;
return diffWhole;
();
// construct and initialize the `std::vector` with size
std::vector<T> vec(size);
// populates the range from [first, end)
std::iota(std::begin(vec), std::end(vec), begin);
return vec;
int main()
for (auto i : ragesof( 5, 9 ))
std::cout << i << ' '; // prints 5 6 7 8
std::cout << 'n';
for (auto i : ragesof(5.1, 9.2))
std::cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
add a comment |
Is there a simple way to create an iterable object that will behave
like a correct for loop onfloat
s?
The simplest hack† would be using the traits std::is_floating_point
to provide different return (i.e. iter <= end
) within the operator!=
overload.
(See Live)
#include <type_traits>
bool operator!=(const iterator& other)
if constexpr (std::is_floating_point_v<T>) return current <= other.current;
return !(*this == other);
†Warning: Even though that does the job, it breaks the meaning of operator!=
overload.
Alternative Solution
The entire range
class can be replaced by a simple function in which the values of the range will be populated with the help of std::iota
in the standard container std::vector
.
Use SFINE, to restrict the use of the function for only the valid types.
This way, you can rely on standard implementations and forget about the reinventions.
(See Live)
#include <iostream>
#include <type_traits>
#include <vector> // std::vector
#include <numeric> // std::iota
#include <cstddef> // std::size_t
#include <cmath> // std::modf
// traits for valid template types(integers and floating points)
template<typename Type>
using is_integers_and_floats = std::conjunction<
std::is_arithmetic<Type>,
std::negation<std::is_same<Type, bool>>,
std::negation<std::is_same<Type, char>>,
std::negation<std::is_same<Type, char16_t>>,
std::negation<std::is_same<Type, char32_t>>,
std::negation<std::is_same<Type, wchar_t>>
/*, std::negation<std::is_same<char8_t, Type>> */ // since C++20
>;
template <typename T>
auto ragesof(const T begin, const T end)
-> std::enable_if_t<is_integers_and_floats<T>::value, std::vector<T>>
if (begin >= end) return std::vector<T>; // edge case to be considered
// find the number of elements between the range
const std::size_t size = [begin, end]() -> std::size_t
const std::size_t diffWhole
= static_cast<std::size_t>(end) - static_cast<std::size_t>(begin);
if constexpr (std::is_floating_point_v<T>)
double whole; // get the decimal parts of begin and end
const double decimalBegin = std::modf(static_cast<double>(begin), &whole);
const double decimalEnd = std::modf(static_cast<double>(end), &whole);
return decimalBegin <= decimalEnd ? diffWhole + 1 : diffWhole;
return diffWhole;
();
// construct and initialize the `std::vector` with size
std::vector<T> vec(size);
// populates the range from [first, end)
std::iota(std::begin(vec), std::end(vec), begin);
return vec;
int main()
for (auto i : ragesof( 5, 9 ))
std::cout << i << ' '; // prints 5 6 7 8
std::cout << 'n';
for (auto i : ragesof(5.1, 9.2))
std::cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
add a comment |
Is there a simple way to create an iterable object that will behave
like a correct for loop onfloat
s?
The simplest hack† would be using the traits std::is_floating_point
to provide different return (i.e. iter <= end
) within the operator!=
overload.
(See Live)
#include <type_traits>
bool operator!=(const iterator& other)
if constexpr (std::is_floating_point_v<T>) return current <= other.current;
return !(*this == other);
†Warning: Even though that does the job, it breaks the meaning of operator!=
overload.
Alternative Solution
The entire range
class can be replaced by a simple function in which the values of the range will be populated with the help of std::iota
in the standard container std::vector
.
Use SFINE, to restrict the use of the function for only the valid types.
This way, you can rely on standard implementations and forget about the reinventions.
(See Live)
#include <iostream>
#include <type_traits>
#include <vector> // std::vector
#include <numeric> // std::iota
#include <cstddef> // std::size_t
#include <cmath> // std::modf
// traits for valid template types(integers and floating points)
template<typename Type>
using is_integers_and_floats = std::conjunction<
std::is_arithmetic<Type>,
std::negation<std::is_same<Type, bool>>,
std::negation<std::is_same<Type, char>>,
std::negation<std::is_same<Type, char16_t>>,
std::negation<std::is_same<Type, char32_t>>,
std::negation<std::is_same<Type, wchar_t>>
/*, std::negation<std::is_same<char8_t, Type>> */ // since C++20
>;
template <typename T>
auto ragesof(const T begin, const T end)
-> std::enable_if_t<is_integers_and_floats<T>::value, std::vector<T>>
if (begin >= end) return std::vector<T>; // edge case to be considered
// find the number of elements between the range
const std::size_t size = [begin, end]() -> std::size_t
const std::size_t diffWhole
= static_cast<std::size_t>(end) - static_cast<std::size_t>(begin);
if constexpr (std::is_floating_point_v<T>)
double whole; // get the decimal parts of begin and end
const double decimalBegin = std::modf(static_cast<double>(begin), &whole);
const double decimalEnd = std::modf(static_cast<double>(end), &whole);
return decimalBegin <= decimalEnd ? diffWhole + 1 : diffWhole;
return diffWhole;
();
// construct and initialize the `std::vector` with size
std::vector<T> vec(size);
// populates the range from [first, end)
std::iota(std::begin(vec), std::end(vec), begin);
return vec;
int main()
for (auto i : ragesof( 5, 9 ))
std::cout << i << ' '; // prints 5 6 7 8
std::cout << 'n';
for (auto i : ragesof(5.1, 9.2))
std::cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
Is there a simple way to create an iterable object that will behave
like a correct for loop onfloat
s?
The simplest hack† would be using the traits std::is_floating_point
to provide different return (i.e. iter <= end
) within the operator!=
overload.
(See Live)
#include <type_traits>
bool operator!=(const iterator& other)
if constexpr (std::is_floating_point_v<T>) return current <= other.current;
return !(*this == other);
†Warning: Even though that does the job, it breaks the meaning of operator!=
overload.
Alternative Solution
The entire range
class can be replaced by a simple function in which the values of the range will be populated with the help of std::iota
in the standard container std::vector
.
Use SFINE, to restrict the use of the function for only the valid types.
This way, you can rely on standard implementations and forget about the reinventions.
(See Live)
#include <iostream>
#include <type_traits>
#include <vector> // std::vector
#include <numeric> // std::iota
#include <cstddef> // std::size_t
#include <cmath> // std::modf
// traits for valid template types(integers and floating points)
template<typename Type>
using is_integers_and_floats = std::conjunction<
std::is_arithmetic<Type>,
std::negation<std::is_same<Type, bool>>,
std::negation<std::is_same<Type, char>>,
std::negation<std::is_same<Type, char16_t>>,
std::negation<std::is_same<Type, char32_t>>,
std::negation<std::is_same<Type, wchar_t>>
/*, std::negation<std::is_same<char8_t, Type>> */ // since C++20
>;
template <typename T>
auto ragesof(const T begin, const T end)
-> std::enable_if_t<is_integers_and_floats<T>::value, std::vector<T>>
if (begin >= end) return std::vector<T>; // edge case to be considered
// find the number of elements between the range
const std::size_t size = [begin, end]() -> std::size_t
const std::size_t diffWhole
= static_cast<std::size_t>(end) - static_cast<std::size_t>(begin);
if constexpr (std::is_floating_point_v<T>)
double whole; // get the decimal parts of begin and end
const double decimalBegin = std::modf(static_cast<double>(begin), &whole);
const double decimalEnd = std::modf(static_cast<double>(end), &whole);
return decimalBegin <= decimalEnd ? diffWhole + 1 : diffWhole;
return diffWhole;
();
// construct and initialize the `std::vector` with size
std::vector<T> vec(size);
// populates the range from [first, end)
std::iota(std::begin(vec), std::end(vec), begin);
return vec;
int main()
for (auto i : ragesof( 5, 9 ))
std::cout << i << ' '; // prints 5 6 7 8
std::cout << 'n';
for (auto i : ragesof(5.1, 9.2))
std::cout << i << ' '; // prints 5.1 6.1 7.1 8.1 9.1
edited May 21 at 8:32
answered May 20 at 9:09
JeJoJeJo
5,75331028
5,75331028
add a comment |
add a comment |
A floating-point loop or iterator should typically use integer types to hold the total number of iterations and the number of the current iteration, and then compute the "loop index" value used within the loop based upon those and loop-invariant floating-point values.
For example:
for (int i=-10; i<=10; i++)
double x = i/10.0; // Substituting i*0.1 would be faster but less accurate
or
for (int i=0; i<=16; i++)
double x = ((startValue*(16-i))+(endValue*i))*(1/16);
Note that there is no possibility of rounding errors affecting the number of iterations. The latter calculation is guaranteed to yield a correctly-rounded result at the endpoints; computing startValue+i*(endValue-startValue)
would likely be faster (since the loop-invariant (endValue-startValue)
can be hoisted) but may be less accurate.
Using an integer iterator along with a function to convert an integer to a floating-point value is probably the most robust way to iterate over a range of floating-point values. Trying to iterate over floating-point values directly is far more likely to yield "off-by-one" errors.
add a comment |
A floating-point loop or iterator should typically use integer types to hold the total number of iterations and the number of the current iteration, and then compute the "loop index" value used within the loop based upon those and loop-invariant floating-point values.
For example:
for (int i=-10; i<=10; i++)
double x = i/10.0; // Substituting i*0.1 would be faster but less accurate
or
for (int i=0; i<=16; i++)
double x = ((startValue*(16-i))+(endValue*i))*(1/16);
Note that there is no possibility of rounding errors affecting the number of iterations. The latter calculation is guaranteed to yield a correctly-rounded result at the endpoints; computing startValue+i*(endValue-startValue)
would likely be faster (since the loop-invariant (endValue-startValue)
can be hoisted) but may be less accurate.
Using an integer iterator along with a function to convert an integer to a floating-point value is probably the most robust way to iterate over a range of floating-point values. Trying to iterate over floating-point values directly is far more likely to yield "off-by-one" errors.
add a comment |
A floating-point loop or iterator should typically use integer types to hold the total number of iterations and the number of the current iteration, and then compute the "loop index" value used within the loop based upon those and loop-invariant floating-point values.
For example:
for (int i=-10; i<=10; i++)
double x = i/10.0; // Substituting i*0.1 would be faster but less accurate
or
for (int i=0; i<=16; i++)
double x = ((startValue*(16-i))+(endValue*i))*(1/16);
Note that there is no possibility of rounding errors affecting the number of iterations. The latter calculation is guaranteed to yield a correctly-rounded result at the endpoints; computing startValue+i*(endValue-startValue)
would likely be faster (since the loop-invariant (endValue-startValue)
can be hoisted) but may be less accurate.
Using an integer iterator along with a function to convert an integer to a floating-point value is probably the most robust way to iterate over a range of floating-point values. Trying to iterate over floating-point values directly is far more likely to yield "off-by-one" errors.
A floating-point loop or iterator should typically use integer types to hold the total number of iterations and the number of the current iteration, and then compute the "loop index" value used within the loop based upon those and loop-invariant floating-point values.
For example:
for (int i=-10; i<=10; i++)
double x = i/10.0; // Substituting i*0.1 would be faster but less accurate
or
for (int i=0; i<=16; i++)
double x = ((startValue*(16-i))+(endValue*i))*(1/16);
Note that there is no possibility of rounding errors affecting the number of iterations. The latter calculation is guaranteed to yield a correctly-rounded result at the endpoints; computing startValue+i*(endValue-startValue)
would likely be faster (since the loop-invariant (endValue-startValue)
can be hoisted) but may be less accurate.
Using an integer iterator along with a function to convert an integer to a floating-point value is probably the most robust way to iterate over a range of floating-point values. Trying to iterate over floating-point values directly is far more likely to yield "off-by-one" errors.
answered May 20 at 18:25
supercatsupercat
58.6k4118158
58.6k4118158
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56217541%2fhow-to-create-a-range-like-iterable-object-of-floats%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Maybe a specialization of
operator==
for floating-point types that subverts the semantics by usingcurrent<=other.current
?– Some programmer dude
May 20 at 9:06
8
What about implementing a special
end
iterator, which would be set inoperator++()
when the incremented value exceedsto
?– Daniel Langr
May 20 at 9:12
Since coroutines have been mentioned, why not use the upcoming ranges library? (Or the range library that was the base for the standard?)
– Some programmer dude
May 20 at 9:21
8
You should be aware that floating-point rounding will affect your loop. For example, with the IEEE-754 format commonly used for
double
,for (double x = 1.03; x <= 11.03; x += 1)
will end whenx
is about 10.03, not 11.03. It will be incremented to 11.030000000000001136868377216160297393798828125, but11.03
in source code becomes the value 11.0299999999999993605115378159098327159881591796875, sox <= 11.03
evaluates to false.– Eric Postpischil
May 20 at 17:41
2
It's a lot safer to use a linspace-style explicit count of elements (and no default count, unlike MATLAB or numpy linspace), rather than starting from a step value and deriving the number of elements from there. A count-oriented instead of step-size-oriented approach eliminates issues with unexpectedly including or excluding the endpoint.
– user2357112
May 20 at 21:41