discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

for statement doesn't do all the steps sometimes

TP
Torsten Paul
Sat, Dec 17, 2016 4:10 PM

On 12/17/2016 04:36 PM, Johan Jonker wrote:

It is the question if it's a bug.
Maybe it is more important to understand it.

No, it's not a bug. It's an issue inherent to how
floating point numbers work in computers. I guess
the only way to get unlimited precision is if you
allow  unlimited memory and unlimited computing
power.

The specific problem with catching the end condition
of the for loop might be possible to solve using
some tricks mentioned here in the forum not long
ago. But this is a specific workaround and will
not solve the "never compare floating point values
for being equal" problem in general.

ciao,
Torsten.

On 12/17/2016 04:36 PM, Johan Jonker wrote: > It is the question if it's a bug. > Maybe it is more important to understand it. > No, it's not a bug. It's an issue inherent to how floating point numbers work in computers. I guess the only way to get unlimited precision is if you allow unlimited memory and unlimited computing power. The specific problem with catching the end condition of the for loop might be possible to solve using some tricks mentioned here in the forum not long ago. But this is a specific workaround and will not solve the "never compare floating point values for being equal" problem in general. ciao, Torsten.
DM
doug moen
Sat, Dec 17, 2016 4:16 PM

OpenSCAD has always worked this way, and this bug is reported multiple
times a year. Last time it was reported on the forum was Nov 21, "For loops
not making sense??".

I have fixed this bug in my version of OpenSCAD, and I've described the
proposed bug fix in issue #1592.

My fix is controversial. This is because there are two mental models for
understanding ranges like [first:step:last].

  1. In the model I use, the 'last' value specifies the final value in the
    range. So [1:0.2:3] means a range that begins at 1 and ends at 3, in steps
    of 0.2. In my implementation, you get the following sequence of numbers:

1
1.2
1.4
1.6
1.8
2
2.2
2.4000000000000004
2.6
2.8
3

(By contrast, OpenSCAD stops at 2.8.)

My implementation accounts for floating point imprecision: the 'last' value
might be an approximation of the final value. So [1 : 0.2 : 2.99] and [1 :
0.2 : 3.01] produce the same result as [1 : 0.2 : 3].

With my design, if you just assume that the 'last' value is the final value
in the range, and write your code as if that is true, then you'll always
get what you expect, and any small floating point inaccuracy in your code
will be compensated for.

  1. I think that some other people think about ranges in terms of the
    underlying implementation. In the current implementation, a range like
    [first:step:last] is equivalent to a C for loop:
    for (i = first; i <= last; i += step)
    If this is your definition of how ranges must work, then [1 : 0.2 : 3] must
    stop at 2.8, because the floating point representation of 0.2 is slightly
    larger than 0.2.

My implementation is a bit more complicated than this, because it
compensates for floating point inaccuracy. The 'last' value is considered
to be an approximation to the final value in the range. The actual final
value is computed as
first + round((last-first)/step) * step
Also, I use multiplication to compute each intermediate value, instead of
repeated addition, to avoid the accumulation of small errors.

If you are the type of person who prefers to think about ranges in terms of
the underlying implementation, then you may not like my implementation,
because it is more complicated.

I personally believe that it is better for the behaviour of ranges to have
a simple high level model that makes sense to beginners, who don't know the
underlying implementation. It's simple to explain that 'last' is the final
value in the range, and it's more complicated to explain the underlying
implementation, and to explain why [1:0.2:3] ends at 2.8.

Doug Moen.

On 17 December 2016 at 02:59, Johan Jonker johangjonker@zonnet.nl wrote:

I found a remarkable difference  in version 2016.10.4 using for statement
with increments smaller than 1. Sometimes the end value is not reached.

 for (a7=[ 6.8:1/5:7.8]) echo(A7=a7);
 for (a=[ 7.2:1/5:8.2]) echo(A=a);

Output is
ECHO: A7 = 6.8
ECHO: A7 = 7
ECHO: A7 = 7.2
ECHO: A7 = 7.4
ECHO: A7 = 7.6
ECHO: A = 7.2
ECHO: A = 7.4
ECHO: A = 7.6
ECHO: A = 7.8
ECHO: A = 8
ECHO: A = 8.2

--
View this message in context: http://forum.openscad.org/for-
statement-doesn-t-do-all-the-steps-sometimes-tp19591.html
Sent from the OpenSCAD mailing list archive at Nabble.com.


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

OpenSCAD has always worked this way, and this bug is reported multiple times a year. Last time it was reported on the forum was Nov 21, "For loops not making sense??". I have fixed this bug in my version of OpenSCAD, and I've described the proposed bug fix in issue #1592. My fix is controversial. This is because there are two mental models for understanding ranges like [first:step:last]. 1. In the model I use, the 'last' value specifies the final value in the range. So [1:0.2:3] means a range that begins at 1 and ends at 3, in steps of 0.2. In my implementation, you get the following sequence of numbers: > 1 > 1.2 > 1.4 > 1.6 > 1.8 > 2 > 2.2 > 2.4000000000000004 > 2.6 > 2.8 > 3 (By contrast, OpenSCAD stops at 2.8.) My implementation accounts for floating point imprecision: the 'last' value might be an approximation of the final value. So [1 : 0.2 : 2.99] and [1 : 0.2 : 3.01] produce the same result as [1 : 0.2 : 3]. With my design, if you just assume that the 'last' value is the final value in the range, and write your code as if that is true, then you'll always get what you expect, and any small floating point inaccuracy in your code will be compensated for. 2. I think that some other people think about ranges in terms of the underlying implementation. In the current implementation, a range like [first:step:last] is equivalent to a C for loop: for (i = first; i <= last; i += step) If this is your definition of how ranges must work, then [1 : 0.2 : 3] must stop at 2.8, because the floating point representation of 0.2 is slightly larger than 0.2. My implementation is a bit more complicated than this, because it compensates for floating point inaccuracy. The 'last' value is considered to be an approximation to the final value in the range. The actual final value is computed as first + round((last-first)/step) * step Also, I use multiplication to compute each intermediate value, instead of repeated addition, to avoid the accumulation of small errors. If you are the type of person who prefers to think about ranges in terms of the underlying implementation, then you may not like my implementation, because it is more complicated. I personally believe that it is better for the behaviour of ranges to have a simple high level model that makes sense to beginners, who don't know the underlying implementation. It's simple to explain that 'last' is the final value in the range, and it's more complicated to explain the underlying implementation, and to explain why [1:0.2:3] ends at 2.8. Doug Moen. On 17 December 2016 at 02:59, Johan Jonker <johangjonker@zonnet.nl> wrote: > I found a remarkable difference in version 2016.10.4 using for statement > with increments smaller than 1. Sometimes the end value is not reached. > > > for (a7=[ 6.8:1/5:7.8]) echo(A7=a7); > for (a=[ 7.2:1/5:8.2]) echo(A=a); > > Output is > ECHO: A7 = 6.8 > ECHO: A7 = 7 > ECHO: A7 = 7.2 > ECHO: A7 = 7.4 > ECHO: A7 = 7.6 > ECHO: A = 7.2 > ECHO: A = 7.4 > ECHO: A = 7.6 > ECHO: A = 7.8 > ECHO: A = 8 > ECHO: A = 8.2 > > > > -- > View this message in context: http://forum.openscad.org/for- > statement-doesn-t-do-all-the-steps-sometimes-tp19591.html > Sent from the OpenSCAD mailing list archive at Nabble.com. > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > > >
A
adrian
Sat, Dec 17, 2016 6:02 PM

The only thing I don't like about your suggestion Doug, is that the user
doesn't have the ability to state the epsilon to use.  That is prolly good
for some, but not so good for others.

--
View this message in context: http://forum.openscad.org/for-statement-doesn-t-do-all-the-steps-sometimes-tp19591p19608.html
Sent from the OpenSCAD mailing list archive at Nabble.com.

The only thing I don't like about your suggestion Doug, is that the user doesn't have the ability to state the epsilon to use. That is prolly good for some, but not so good for others. -- View this message in context: http://forum.openscad.org/for-statement-doesn-t-do-all-the-steps-sometimes-tp19591p19608.html Sent from the OpenSCAD mailing list archive at Nabble.com.
JD
Jerry Davis
Sat, Dec 17, 2016 6:48 PM

Doug,

Given your statement:
"I personally believe that it is better for the behaviour of ranges to
have a simple high level model that makes sense to beginners, who don't
know the underlying implementation. It's simple to explain that 'last' is
the final value in the range
, and it's more complicated to explain the
underlying implementation, and to explain why [1:0.2:3] ends at 2.8."

If you look at the actual documentation on OpenSCAD.
end - stop when next value would be past end

I personally believe the first part of your statement is how the
documentation says its supposed to work. That is my opinion, however.

Jerry
For loop

Evaluate each value in a range or vector, applying it to the following
Action.

for(variable = [start : increment : end])
for(variable = [start : end])
for(variable = [vector])

parameters
As a range [ start : <increment : > end ] (see section on range
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/General#Ranges)Note:
For range, values are separated by colons rather than commas used in
vectors.*start - initial value
increment* or step - amount to increase
the value, optional, default = 1**end - stop when next value would be
past end

--
Extra Ham Operator: K7AZJ
Registered Linux User: 275424
Raspberry Pi and Openscad developer

The most exciting phrase to hear in science - the one that heralds new
discoveries - is not "Eureka!" but "That's funny...".
- Isaac. Asimov

On Sat, Dec 17, 2016 at 11:02 AM, adrian adrianh.bsc@gmail.com wrote:

The only thing I don't like about your suggestion Doug, is that the user
doesn't have the ability to state the epsilon to use.  That is prolly good
for some, but not so good for others.

--
View this message in context: http://forum.openscad.org/for-
statement-doesn-t-do-all-the-steps-sometimes-tp19591p19608.html
Sent from the OpenSCAD mailing list archive at Nabble.com.


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

Doug, Given your statement: "*I personally believe that it is better for the behaviour of ranges to have a simple high level model that makes sense to beginners, who don't know the underlying implementation. It's simple to explain that 'last' is the final value in the range*, and it's more complicated to explain the underlying implementation, and to explain why [1:0.2:3] ends at 2.8." If you look at the actual documentation on OpenSCAD. *end* - stop when next value would be past end I personally believe the first part of your statement is how the documentation says its supposed to work. That is my opinion, however. Jerry For loop Evaluate each value in a range or vector, applying it to the following Action. for(variable = [start : increment : end]) for(variable = [start : end]) for(variable = [vector]) *parameters* As a range *[* start *:* <increment *:* > end *]* (see section on range <https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/General#Ranges>)*Note: For range, values are separated by colons rather than commas used in vectors.**start* - initial value*increment* or step - amount to increase the value, *optional, default = 1**end* - stop when next value would be past end -- Extra Ham Operator: K7AZJ Registered Linux User: 275424 Raspberry Pi and Openscad developer *The most exciting phrase to hear in science - the one that heralds new discoveries - is not "Eureka!" but "That's funny...".*- Isaac. Asimov On Sat, Dec 17, 2016 at 11:02 AM, adrian <adrianh.bsc@gmail.com> wrote: > The only thing I don't like about your suggestion Doug, is that the user > doesn't have the ability to state the epsilon to use. That is prolly good > for some, but not so good for others. > > > > -- > View this message in context: http://forum.openscad.org/for- > statement-doesn-t-do-all-the-steps-sometimes-tp19591p19608.html > Sent from the OpenSCAD mailing list archive at Nabble.com. > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >
DM
doug moen
Sat, Dec 17, 2016 10:19 PM

Adrian said "The only thing I don't like about your suggestion Doug, is
that the user
doesn't have the ability to state the epsilon to use."

I would vote no to epsilons.

Right now, [a:step:b] is a closed range that includes both a and b, most of
the time.
But it's unreliable, and sometimes b is omitted, and it becomes a half-open
range.

This is a bug, and to work around the bug, you add an epsilon to b.
For example, Johan worked around the bug like this:
[ 6.8 : 1/5 : 7.8 + 0.000001]
where 0.000001 is an explicit epsilon value.

In order to understand why an epsilon is sometimes needed, and what value
to use
as epsilon, you have to know the internal implementation details.

The goal of my bug fix is to eliminate the need for epsilons, and to
eliminate the
need to understand internal implementation details. [x:step:y] is always a
closed
range that includes both x and y. It works reliably, not just "most of the
time".

If we introduced a syntax like [first:step:last:epsilon], then what would
that even
mean? Is it a closed range, a half open range, what is it? I assume that the
meaning would be complex and would require a full understanding of the
implementation, which is what I'm trying to avoid.

I will also note that no other programming language has a range syntax that
includes an epsilon specification.

On 17 December 2016 at 13:02, adrian adrianh.bsc@gmail.com wrote:

The only thing I don't like about your suggestion Doug, is that the user
doesn't have the ability to state the epsilon to use.  That is prolly good
for some, but not so good for others.

--
View this message in context: http://forum.openscad.org/for-
statement-doesn-t-do-all-the-steps-sometimes-tp19591p19608.html
Sent from the OpenSCAD mailing list archive at Nabble.com.


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

Adrian said "The only thing I don't like about your suggestion Doug, is that the user doesn't have the ability to state the epsilon to use." I would vote no to epsilons. Right now, [a:step:b] is a closed range that includes both a and b, most of the time. But it's unreliable, and sometimes b is omitted, and it becomes a half-open range. This is a bug, and to work around the bug, you add an epsilon to b. For example, Johan worked around the bug like this: [ 6.8 : 1/5 : 7.8 + 0.000001] where 0.000001 is an explicit epsilon value. In order to understand why an epsilon is sometimes needed, and what value to use as epsilon, you have to know the internal implementation details. The goal of my bug fix is to eliminate the need for epsilons, and to eliminate the need to understand internal implementation details. [x:step:y] is always a closed range that includes both x and y. It works reliably, not just "most of the time". If we introduced a syntax like [first:step:last:epsilon], then what would that even mean? Is it a closed range, a half open range, what is it? I assume that the meaning would be complex and would require a full understanding of the implementation, which is what I'm trying to avoid. I will also note that no other programming language has a range syntax that includes an epsilon specification. On 17 December 2016 at 13:02, adrian <adrianh.bsc@gmail.com> wrote: > The only thing I don't like about your suggestion Doug, is that the user > doesn't have the ability to state the epsilon to use. That is prolly good > for some, but not so good for others. > > > > -- > View this message in context: http://forum.openscad.org/for- > statement-doesn-t-do-all-the-steps-sometimes-tp19591p19608.html > Sent from the OpenSCAD mailing list archive at Nabble.com. > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > > >
JL
Jean-Paul Louis
Sun, Dec 18, 2016 1:49 AM

Forum users and developers,

First let me state that you have done a great job with OpenSCAD.

But this unending complaint about real number indices for loop has nothing
to do with design intent.
This is why we should not let computer scientist define the specs
of a program.
It is too bad that software writers try to be too smart, and try to get
away with the limitations of our computing machines as Karsten stated it
very clearly.

By design, any loop mechanism should have the index being INTEGER ONLY.
Then the parameters that matters would be computed inside the loop,
so rounding errors due to the implementation would be minimized.

But remember, most of the user are not computer scientists, they are
people trying to do 3D modeling with a tool given to them for free.

The best way to avoid major criticism would be to allow only integer
indices, and provide an error if they are not.

When I started as a young engineer, the tools of choice were pencil, paper and
a slide rule.
So I learned very fast that many decimals are not important, what is important
is just a few significant decimals (3 significant digit with a slide rule), but
the order of magnitude has to be right.
This drove me to understand that calculators are more of a nuisance than a help
in mechanical design which is what we should worry about.
So please, get rid of real numbers in loop index, and just document it.
You will see the amount of complaints decrease very fast.

Just my two cents.

On Dec 17, 2016, at 4:09 PM, r.d. terramir terramircomputers@gmail.com wrote:

It is really proper use of floating point math, nothing OpenSCAD specific.

I always use integer indices and compute everything from those. I think it has been mentioned before that OpenSCAD could do that internally. I.e. instead of repeatedly adding it could have a hidden counter that it multiples the step value by.

echo(6.8 + 5 * 0.2 - 7.8);
ECHO: 0

That would give more accurate results but there would still be caveats.

On 17 December 2016 at 15:36, Johan Jonker johangjonker@zonnet.nl wrote:
It is the question if it's a bug.
Maybe it is more important to understand it.

I tried to do the same in VBA.
There the same thing happens.

I more like a warning in the on line manual about the proper use of the
statement.

--
View this message in context: http://forum.openscad.org/for-statement-doesn-t-do-all-the-steps-sometimes-tp19591p19603.html
Sent from the OpenSCAD mailing list archive at Nabble.com.


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

Forum users and developers, First let me state that you have done a great job with OpenSCAD. But this unending complaint about real number indices for loop has nothing to do with design intent. This is why we should not let computer scientist define the specs of a program. It is too bad that software writers try to be too smart, and try to get away with the limitations of our computing machines as Karsten stated it very clearly. By design, any loop mechanism should have the index being INTEGER ONLY. Then the parameters that matters would be computed inside the loop, so rounding errors due to the implementation would be minimized. But remember, most of the user are not computer scientists, they are people trying to do 3D modeling with a tool given to them for free. The best way to avoid major criticism would be to allow only integer indices, and provide an error if they are not. When I started as a young engineer, the tools of choice were pencil, paper and a slide rule. So I learned very fast that many decimals are not important, what is important is just a few significant decimals (3 significant digit with a slide rule), but the order of magnitude has to be right. This drove me to understand that calculators are more of a nuisance than a help in mechanical design which is what we should worry about. So please, get rid of real numbers in loop index, and just document it. You will see the amount of complaints decrease very fast. Just my two cents. > On Dec 17, 2016, at 4:09 PM, r.d. terramir <terramircomputers@gmail.com> wrote: > > It is really proper use of floating point math, nothing OpenSCAD specific. > > I always use integer indices and compute everything from those. I think it has been mentioned before that OpenSCAD could do that internally. I.e. instead of repeatedly adding it could have a hidden counter that it multiples the step value by. > > echo(6.8 + 5 * 0.2 - 7.8); > ECHO: 0 > > That would give more accurate results but there would still be caveats. > > > On 17 December 2016 at 15:36, Johan Jonker <johangjonker@zonnet.nl> wrote: > It is the question if it's a bug. > Maybe it is more important to understand it. > > I tried to do the same in VBA. > There the same thing happens. > > I more like a warning in the on line manual about the proper use of the > statement. > > > > > -- > View this message in context: http://forum.openscad.org/for-statement-doesn-t-do-all-the-steps-sometimes-tp19591p19603.html > Sent from the OpenSCAD mailing list archive at Nabble.com. > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
P
Parkinbot
Sun, Dec 18, 2016 12:09 PM

Doug,

again a vote for silent behavior and hidden magic - and no means to roll
back. You propose a solution that will potentially break existing code and
algorithms. The semantical difference can be a "hard to spot" problem as
soon as nested loops come into play and can affect each (!) nesting level.

Look at this double loop, (where only the inner loop is effected, as the
outer loop is classical):

first = 6.8;
step = .2;
last = 8;

for (y=[7.4:.05:8.4]) loops(y);

module loops(last)
{
echo(classic = classic_loop(first, step, last));
echo(dougs = doug_loop(first, step, last));
echo();
}

function classic_loop(first, step, last) = [for(x = [first: step: last])
x];

function doug_loop(first, step, last) =
let(first_ = 0)
let(step_ = sign(step))
let(last_ = round((last-first)/step))
[for(i = [first_: step_: last_]) first+i*step];

this is the output:

ECHO: classic = [6.8, 7, 7.2, 7.4]
ECHO: dougs = [6.8, 7, 7.2, 7.4]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4]
ECHO: dougs = [6.8, 7, 7.2, 7.4]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2, 8.4]
ECHO:
ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2]
ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2, 8.4]
ECHO:

--
View this message in context: http://forum.openscad.org/for-statement-doesn-t-do-all-the-steps-sometimes-tp19591p19622.html
Sent from the OpenSCAD mailing list archive at Nabble.com.

Doug, again a vote for silent behavior and hidden magic - and no means to roll back. You propose a solution that will potentially break existing code and algorithms. The semantical difference can be a "hard to spot" problem as soon as nested loops come into play and can affect each (!) nesting level. Look at this double loop, (where only the inner loop is effected, as the outer loop is classical): > first = 6.8; > step = .2; > last = 8; > > for (y=[7.4:.05:8.4]) loops(y); > > module loops(last) > { > echo(classic = classic_loop(first, step, last)); > echo(dougs = doug_loop(first, step, last)); > echo(); > } > > function classic_loop(first, step, last) = [for(x = [first: step: last]) > x]; > > function doug_loop(first, step, last) = > let(first_ = 0) > let(step_ = sign(step)) > let(last_ = round((last-first)/step)) > [for(i = [first_: step_: last_]) first+i*step]; this is the output: > ECHO: classic = [6.8, 7, 7.2, 7.4] > ECHO: dougs = [6.8, 7, 7.2, 7.4] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4] > ECHO: dougs = [6.8, 7, 7.2, 7.4] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2, 8.4] > ECHO: > ECHO: classic = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2] > ECHO: dougs = [6.8, 7, 7.2, 7.4, 7.6, 7.8, 8, 8.2, 8.4] > ECHO: -- View this message in context: http://forum.openscad.org/for-statement-doesn-t-do-all-the-steps-sometimes-tp19591p19622.html Sent from the OpenSCAD mailing list archive at Nabble.com.
JJ
Johan Jonker
Sun, Dec 18, 2016 1:25 PM

Interesting to read the discussion and solutions, thanks for that

Speaking for myself:
I assume that the for loop is originally made to be used to count a specific
number of steps like in- for (i = [1:100])

On the other hand I do not always like the no-functional names i or count
and prefer an functional named variable, like this:
function circle(r) = [ for (angle  [0:360])  [rsin(angle), rcos(angle)]];

The problem is that I want to have a while statement that performs
increaments until a limit value is reached.
And sometimes these limits values are  floating point values.
-for- loops can be used to behave like that but you need to understand how
they behave with floating point values. I encountered the behaviour while
debugging a 3D design. The same limits exist in other programming languages
but I never encountered it there.

I now use this solution in the cross-section of my saxophone mouthpiece
design where beta an alfa are dynamic transitions in the cross-section (they
are visible in the  figure):

for (angle= [each [beta:(180-2beta)/45:180-beta+0.1],
each [180-beta:(beta-alfa)/45:180-alfa+0.1],
each [180-alfa:(180+2
alfa)/45:360+alfa+0.1],
each [360+alfa: (beta-alfa)/45:360+beta-0.1]])

http://forum.openscad.org/file/n19623/mp.jpg

--
View this message in context: http://forum.openscad.org/for-statement-doesn-t-do-all-the-steps-sometimes-tp19591p19623.html
Sent from the OpenSCAD mailing list archive at Nabble.com.

Interesting to read the discussion and solutions, thanks for that Speaking for myself: I assume that the for loop is originally made to be used to count a specific number of steps like in- for (i = [1:100]) On the other hand I do not always like the no-functional names i or count and prefer an functional named variable, like this: function circle(r) = [ for (angle [0:360]) [r*sin(angle), r*cos(angle)]]; The problem is that I want to have a while statement that performs increaments until a limit value is reached. And sometimes these limits values are floating point values. -for- loops can be used to behave like that but you need to understand how they behave with floating point values. I encountered the behaviour while debugging a 3D design. The same limits exist in other programming languages but I never encountered it there. I now use this solution in the cross-section of my saxophone mouthpiece design where beta an alfa are dynamic transitions in the cross-section (they are visible in the figure): for (angle= [each [beta:(180-2*beta)/45:180-beta+0.1], each [180-beta:(beta-alfa)/45:180-alfa+0.1], each [180-alfa:(180+2*alfa)/45:360+alfa+0.1], each [360+alfa: (beta-alfa)/45:360+beta-0.1]]) <http://forum.openscad.org/file/n19623/mp.jpg> -- View this message in context: http://forum.openscad.org/for-statement-doesn-t-do-all-the-steps-sometimes-tp19591p19623.html Sent from the OpenSCAD mailing list archive at Nabble.com.
RW
Rogier Wolff
Mon, Dec 19, 2016 9:29 AM

On Sat, Dec 17, 2016 at 11:16:40AM -0500, doug moen wrote:

My implementation accounts for floating point imprecision: the 'last' value
might be an approximation of the final value. So [1 : 0.2 : 2.99] and [1 :
0.2 : 3.01] produce the same result as [1 : 0.2 : 3].

Auch!  I know that steps of 0.2 are "inaccurate" and "compensation for
floating point inaccuracies may be necessary". But if I want N steps
where the step size is never smaller than 0.01, and I do NOT want to
include the final object at index "3", I'll write:
[1:3/N:2.99] to make sure that the final one will NOT be rendered.

Fixing the "assumption" that 3/N is never < 0.01, I'll write:
[1:3/N:3-1/N], again to make sure that I don't include the final
one at (floating point accuracy equal to) 3.

This is important if, say I'm building a fence:
N=4;
eps=0.1;
for (i=[0:1/N:3-eps]) translate ([i,0,0]) cube ([0.1,0.1,1]);
for (i=[0:1/N:1-eps]) translate ([3,i,0]) cube ([0.1,0.1,1]);
for (i=[4:1/N:5-eps]) translate ([i,1,0]) cube ([0.1,0.1,1]);

The final values each time are meant NOT to be included each time,
otherwise I get a double post at those points.

Roger.

--
** R.E.Wolff@BitWizard.nl ** http://www.BitWizard.nl/ ** +31-15-2600998 **
**    Delftechpark 26 2628 XH  Delft, The Netherlands. KVK: 27239233    **
-- BitWizard writes Linux device drivers for any device you may have! --
The plan was simple, like my brother-in-law Phil. But unlike
Phil, this plan just might work.

On Sat, Dec 17, 2016 at 11:16:40AM -0500, doug moen wrote: > My implementation accounts for floating point imprecision: the 'last' value > might be an approximation of the final value. So [1 : 0.2 : 2.99] and [1 : > 0.2 : 3.01] produce the same result as [1 : 0.2 : 3]. Auch! I know that steps of 0.2 are "inaccurate" and "compensation for floating point inaccuracies may be necessary". But if I want N steps where the step size is never smaller than 0.01, and I do NOT want to include the final object at index "3", I'll write: [1:3/N:2.99] to make sure that the final one will NOT be rendered. Fixing the "assumption" that 3/N is never < 0.01, I'll write: [1:3/N:3-1/N], again to make sure that I don't include the final one at (floating point accuracy equal to) 3. This is important if, say I'm building a fence: N=4; eps=0.1; for (i=[0:1/N:3-eps]) translate ([i,0,0]) cube ([0.1,0.1,1]); for (i=[0:1/N:1-eps]) translate ([3,i,0]) cube ([0.1,0.1,1]); for (i=[4:1/N:5-eps]) translate ([i,1,0]) cube ([0.1,0.1,1]); The final values each time are meant NOT to be included each time, otherwise I get a double post at those points. Roger. -- ** R.E.Wolff@BitWizard.nl ** http://www.BitWizard.nl/ ** +31-15-2600998 ** ** Delftechpark 26 2628 XH Delft, The Netherlands. KVK: 27239233 ** *-- BitWizard writes Linux device drivers for any device you may have! --* The plan was simple, like my brother-in-law Phil. But unlike Phil, this plan just might work.
DM
doug moen
Mon, Dec 19, 2016 10:02 PM

The fix I implemented is based on the Haskell implementation of floating
point numeric ranges. That was the best reference implementation that I
could find at the time.

The first:step:last syntax is taken from Matlab. I found Matlab source code
for the colon operator, it's here:
https://gist.github.com/Juanlu001/7383894

This fixes the bug, and it is more backward compatible with OpenSCAD than
the Haskell implementation.

On 17 December 2016 at 02:59, Johan Jonker johangjonker@zonnet.nl wrote:

I found a remarkable difference  in version 2016.10.4 using for statement
with increments smaller than 1. Sometimes the end value is not reached.

 for (a7=[ 6.8:1/5:7.8]) echo(A7=a7);
 for (a=[ 7.2:1/5:8.2]) echo(A=a);

Output is
ECHO: A7 = 6.8
ECHO: A7 = 7
ECHO: A7 = 7.2
ECHO: A7 = 7.4
ECHO: A7 = 7.6
ECHO: A = 7.2
ECHO: A = 7.4
ECHO: A = 7.6
ECHO: A = 7.8
ECHO: A = 8
ECHO: A = 8.2

--
View this message in context: http://forum.openscad.org/for-
statement-doesn-t-do-all-the-steps-sometimes-tp19591.html
Sent from the OpenSCAD mailing list archive at Nabble.com.


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

The fix I implemented is based on the Haskell implementation of floating point numeric ranges. That was the best reference implementation that I could find at the time. The first:step:last syntax is taken from Matlab. I found Matlab source code for the colon operator, it's here: https://gist.github.com/Juanlu001/7383894 This fixes the bug, *and* it is more backward compatible with OpenSCAD than the Haskell implementation. On 17 December 2016 at 02:59, Johan Jonker <johangjonker@zonnet.nl> wrote: > I found a remarkable difference in version 2016.10.4 using for statement > with increments smaller than 1. Sometimes the end value is not reached. > > > for (a7=[ 6.8:1/5:7.8]) echo(A7=a7); > for (a=[ 7.2:1/5:8.2]) echo(A=a); > > Output is > ECHO: A7 = 6.8 > ECHO: A7 = 7 > ECHO: A7 = 7.2 > ECHO: A7 = 7.4 > ECHO: A7 = 7.6 > ECHO: A = 7.2 > ECHO: A = 7.4 > ECHO: A = 7.6 > ECHO: A = 7.8 > ECHO: A = 8 > ECHO: A = 8.2 > > > > -- > View this message in context: http://forum.openscad.org/for- > statement-doesn-t-do-all-the-steps-sometimes-tp19591.html > Sent from the OpenSCAD mailing list archive at Nabble.com. > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > > >