discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Loop precision (==floating point precision ?)

V
vicnet
Thu, Nov 19, 2015 12:35 PM

Hello,

This:

outputs 0.95 at last line and not 1.

(now I use a integer from 0 to 100 and divide after)

Is it normal ?

a+
Vicnet

--
View this message in context: http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642.html
Sent from the OpenSCAD mailing list archive at Nabble.com.

Hello, This: outputs 0.95 at last line and not 1. (now I use a integer from 0 to 100 and divide after) Is it normal ? a+ Vicnet -- View this message in context: http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642.html Sent from the OpenSCAD mailing list archive at Nabble.com.
B
blobule
Thu, Nov 19, 2015 1:00 PM

Yes this is normal.

In your loop:

for (t=[0.0:0.05:1.0]) echo(t);

The variable t is compared exactly with 1.0, and exact comparison is not
reliable for floating point values.
Basically, you should never use float inside a FOR loop, unless you add a
small value to make sure it stops where you want.

My suggestions for a reliable version of your loop is:

for (t=[0.0:0.05:1.0001]) echo(t);

or better, use integers:
for(i=[0:20]) { t=i*0.05;echo(t); }

In the first example, the extra .0001 reminds you that you are taking into
account the fact that floating numbers are approximate.
The second example is the best way, in my opinion, to loop trough a
sequence. Use an integer to count the steps, and derive all float values
from that integer.

If you want to make sure that your loop will always stop at 1.0 for any step
(s), then do this:
s=0.05; // step size
for (t=[0.0:s:1.0+s/2]) echo(t);

You are essentially adding 1/2 of one step to the end value, to make sure
you get that value.

I hope this helps!

--
View this message in context: http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642p14643.html
Sent from the OpenSCAD mailing list archive at Nabble.com.

Yes this is normal. In your loop: for (t=[0.0:0.05:1.0]) echo(t); The variable t is compared *exactly* with 1.0, and exact comparison is not reliable for floating point values. Basically, you should never use float inside a FOR loop, unless you add a small value to make sure it stops where you want. My suggestions for a reliable version of your loop is: for (t=[0.0:0.05:1.0001]) echo(t); or better, use integers: for(i=[0:20]) { t=i*0.05;echo(t); } In the first example, the extra .0001 reminds you that you are taking into account the fact that floating numbers are approximate. The second example is the best way, in my opinion, to loop trough a sequence. Use an integer to count the steps, and derive all float values from that integer. If you want to make sure that your loop will always stop at 1.0 for any step (s), then do this: s=0.05; // step size for (t=[0.0:s:1.0+s/2]) echo(t); You are essentially adding 1/2 of one step to the end value, to make sure you get that value. I hope this helps! -- View this message in context: http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642p14643.html Sent from the OpenSCAD mailing list archive at Nabble.com.
DM
doug moen
Thu, Nov 19, 2015 4:29 PM

I do not recommend using a trick like
for (t=[0.0:0.05:1.0001])
to make a loop terminate.

0.05 can only be represented approximately as a binary floating point
number.
The error accumulates in each step of the loop.
This ever increasing error could affect the accuracy of your model,
depending on how the loop value is used.

It is better to iterate over integers, and use division to construct a
fraction
in each iteration of the loop.

On 19 November 2015 at 08:00, blobule roys@iro.umontreal.ca wrote:

Yes this is normal.

In your loop:

 for (t=[0.0:0.05:1.0]) echo(t);

The variable t is compared exactly with 1.0, and exact comparison is not
reliable for floating point values.
Basically, you should never use float inside a FOR loop, unless you add a
small value to make sure it stops where you want.

My suggestions for a reliable version of your loop is:

 for (t=[0.0:0.05:1.0001]) echo(t);

or better, use integers:
for(i=[0:20]) { t=i*0.05;echo(t); }

In the first example, the extra .0001 reminds you that you are taking into
account the fact that floating numbers are approximate.
The second example is the best way, in my opinion, to loop trough a
sequence. Use an integer to count the steps, and derive all float values
from that integer.

If you want to make sure that your loop will always stop at 1.0 for any
step
(s), then do this:
s=0.05; // step size
for (t=[0.0:s:1.0+s/2]) echo(t);

You are essentially adding 1/2 of one step to the end value, to make sure
you get that value.

I hope this helps!

--
View this message in context:
http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642p14643.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

I do not recommend using a trick like for (t=[0.0:0.05:1.0001]) to make a loop terminate. 0.05 can only be represented approximately as a binary floating point number. The error accumulates in each step of the loop. This ever increasing error could affect the accuracy of your model, depending on how the loop value is used. It is better to iterate over integers, and use division to construct a fraction in each iteration of the loop. On 19 November 2015 at 08:00, blobule <roys@iro.umontreal.ca> wrote: > Yes this is normal. > > In your loop: > > for (t=[0.0:0.05:1.0]) echo(t); > > The variable t is compared *exactly* with 1.0, and exact comparison is not > reliable for floating point values. > Basically, you should never use float inside a FOR loop, unless you add a > small value to make sure it stops where you want. > > My suggestions for a reliable version of your loop is: > > for (t=[0.0:0.05:1.0001]) echo(t); > or better, use integers: > for(i=[0:20]) { t=i*0.05;echo(t); } > > In the first example, the extra .0001 reminds you that you are taking into > account the fact that floating numbers are approximate. > The second example is the best way, in my opinion, to loop trough a > sequence. Use an integer to count the steps, and derive all float values > from that integer. > > If you want to make sure that your loop will always stop at 1.0 for any > step > (s), then do this: > s=0.05; // step size > for (t=[0.0:s:1.0+s/2]) echo(t); > > You are essentially adding 1/2 of one step to the end value, to make sure > you get that value. > > I hope this helps! > > > > > -- > View this message in context: > http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642p14643.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 > > >
V
vicnet
Thu, Nov 19, 2015 4:41 PM

Thanks all.
I change iterator to integer and add a 'let' function to calculate the
parameter by dividing.
a+
Vicnet

--
View this message in context: http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642p14649.html
Sent from the OpenSCAD mailing list archive at Nabble.com.

Thanks all. I change iterator to integer and add a 'let' function to calculate the parameter by dividing. a+ Vicnet -- View this message in context: http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642p14649.html Sent from the OpenSCAD mailing list archive at Nabble.com.
C
ctchin
Thu, Nov 19, 2015 5:46 PM

With the most recent update, let() is no longer necessary in for() or other
flow control structure.  You only need let() in list comprehension. (and
function definition?)

I still feel something is off... I get that IEEE double has finite
resolution, but failing to terminate a for loop correctly with a precision
of 1/25 that's OUCH.

--
View this message in context: http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642p14650.html
Sent from the OpenSCAD mailing list archive at Nabble.com.

With the most recent update, let() is no longer necessary in for() or other flow control structure. You only need let() in list comprehension. (and function definition?) I still feel something is off... I get that IEEE double has finite resolution, but failing to terminate a for loop correctly with a precision of 1/25 that's OUCH. -- View this message in context: http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642p14650.html Sent from the OpenSCAD mailing list archive at Nabble.com.
HZ
Henner Zeller
Thu, Nov 19, 2015 6:08 PM

On 19 November 2015 at 09:46, ctchin c.t.chin@szu.edu.cn wrote:

With the most recent update, let() is no longer necessary in for() or other
flow control structure.  You only need let() in list comprehension. (and
function definition?)

I still feel something is off... I get that IEEE double has finite
resolution, but failing to terminate a for loop correctly with a precision
of 1/25 that's OUCH.

read the loop: the run variable is compared with 1.0. if it only
arrives the tiniest fraction above the cutoff point, then 1 is never
reached.
(so 1.000000001 for instance). So it is not 1/25 but about 1/(2^52) =
1/4503599627370496 being off which makes the loop not terminate at
1.0.

On 19 November 2015 at 09:46, ctchin <c.t.chin@szu.edu.cn> wrote: > With the most recent update, let() is no longer necessary in for() or other > flow control structure. You only need let() in list comprehension. (and > function definition?) > > I still feel something is off... I get that IEEE double has finite > resolution, but failing to terminate a for loop correctly with a precision > of 1/25 that's OUCH. read the loop: the run variable is compared with 1.0. if it only arrives the tiniest fraction above the cutoff point, then 1 is never reached. (so 1.000000001 for instance). So it is not 1/25 but about 1/(2^52) = 1/4503599627370496 being off which makes the loop not terminate at 1.0. > > > > > -- > View this message in context: http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642p14650.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
Thu, Nov 19, 2015 6:14 PM

ctchin:

0.05 cannot be represented exactly in binary floating point, regardless of
the precision. We happen to use 64 bit floats, but it doesn't matter how
many bits you have, it still can't be represented. We could use 1024 bit
floats, and the loop would still not terminate at 1. It's not a bug in
OpenSCAD, it is just how floating point works.

If we used decimal floating point, then 0.05 would have an exact
representation, but there's no hardware support for decimal floating point
on the platforms we support, so OpenSCAD would be a lot slower. Decimal
floating point also does not eliminate most of the weirdness associated
with floating point, it only addresses the problem of floating point
literals like 0.05 not having an exact representation.

The article "What Every Computer Scientist Should Know About Floating
Point" contains way more information than most OpenSCAD programmers would
probably care to know, but it's a classic reference and is good for anybody
who really wants to know the details about why float arithmetic is so
unintuitive, and how to write programs to compensate for the weirdness.
https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

The 'let' operator only works in expressions and in list comprehensions,
and is the only way to introduce local variable bindings in those contexts,
so your comment about 'let is no longer necessary' seems to be inaccurate.
Maybe you are thinking about the deprecation of the 'assign' operator.

On 19 November 2015 at 12:46, ctchin c.t.chin@szu.edu.cn wrote:

With the most recent update, let() is no longer necessary in for() or other
flow control structure.  You only need let() in list comprehension. (and
function definition?)

I still feel something is off... I get that IEEE double has finite
resolution, but failing to terminate a for loop correctly with a precision
of 1/25 that's OUCH.

--
View this message in context:
http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642p14650.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

ctchin: 0.05 cannot be represented exactly in binary floating point, regardless of the precision. We happen to use 64 bit floats, but it doesn't matter how many bits you have, it still can't be represented. We could use 1024 bit floats, and the loop would still not terminate at 1. It's not a bug in OpenSCAD, it is just how floating point works. If we used decimal floating point, then 0.05 would have an exact representation, but there's no hardware support for decimal floating point on the platforms we support, so OpenSCAD would be a lot slower. Decimal floating point also does not eliminate most of the weirdness associated with floating point, it only addresses the problem of floating point literals like 0.05 not having an exact representation. The article "What Every Computer Scientist Should Know About Floating Point" contains way more information than most OpenSCAD programmers would probably care to know, but it's a classic reference and is good for anybody who really wants to know the details about why float arithmetic is so unintuitive, and how to write programs to compensate for the weirdness. https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html The 'let' operator only works in expressions and in list comprehensions, and is the only way to introduce local variable bindings in those contexts, so your comment about 'let is no longer necessary' seems to be inaccurate. Maybe you are thinking about the deprecation of the 'assign' operator. On 19 November 2015 at 12:46, ctchin <c.t.chin@szu.edu.cn> wrote: > With the most recent update, let() is no longer necessary in for() or other > flow control structure. You only need let() in list comprehension. (and > function definition?) > > I still feel something is off... I get that IEEE double has finite > resolution, but failing to terminate a for loop correctly with a precision > of 1/25 that's OUCH. > > > > > -- > View this message in context: > http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642p14650.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 > > >
MK
Marius Kintel
Thu, Nov 19, 2015 6:25 PM

On Nov 19, 2015, at 12:46 PM, ctchin c.t.chin@szu.edu.cn wrote:

still feel something is off... I get that IEEE double has finite
resolution, but failing to terminate a for loop correctly with a precision
of 1/25 that's OUCH.

Did I misunderstand something? I was under the impression that the loop indeed terminates, it’s just sometimes missing the last step since we use equality comparison.

In retrospect, we should perhaps have excluded the max range item from the range, but that’s a tough one to revert now due to the amount of existing designs using this.

-Marius

> On Nov 19, 2015, at 12:46 PM, ctchin <c.t.chin@szu.edu.cn> wrote: > > still feel something is off... I get that IEEE double has finite > resolution, but failing to terminate a for loop correctly with a precision > of 1/25 that's OUCH. > Did I misunderstand something? I was under the impression that the loop indeed terminates, it’s just sometimes missing the last step since we use equality comparison. In retrospect, we should perhaps have excluded the max range item from the range, but that’s a tough one to revert now due to the amount of existing designs using this. -Marius
W
whosawhatsis
Thu, Nov 19, 2015 6:59 PM

I've wished for an exclusive end value for a long time. Perhaps there could be some prefix to mark the value as exclusive? Something like "for(i = [0:2:!5])" maybe, though '!' probably isn't the best character to use.

You could also have an extra term, more like regex syntax, though you'd probably still want to use a special character to avoid collisions with variable names. Maybe just an extra trailing colon, like "for(i = [0:2:5:])"?

On Thursday, November 19, 2015 at 10:25, Marius Kintel wrote:

On Nov 19, 2015, at 12:46 PM, ctchin <c.t.chin@szu.edu.cn (mailto:c.t.chin@szu.edu.cn)> wrote:

still feel something is off... I get that IEEE double has finite
resolution, but failing to terminate a for loop correctly with a precision
of 1/25 that's OUCH.

Did I misunderstand something? I was under the impression that the loop indeed terminates, it’s just sometimes missing the last step since we use equality comparison.

In retrospect, we should perhaps have excluded the max range item from the range, but that’s a tough one to revert now due to the amount of existing designs using this.

-Marius


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

I've wished for an exclusive end value for a long time. Perhaps there could be some prefix to mark the value as exclusive? Something like "for(i = [0:2:!5])" maybe, though '!' probably isn't the best character to use. You could also have an extra term, more like regex syntax, though you'd probably still want to use a special character to avoid collisions with variable names. Maybe just an extra trailing colon, like "for(i = [0:2:5:])"? On Thursday, November 19, 2015 at 10:25, Marius Kintel wrote: > > On Nov 19, 2015, at 12:46 PM, ctchin <c.t.chin@szu.edu.cn (mailto:c.t.chin@szu.edu.cn)> wrote: > > > > still feel something is off... I get that IEEE double has finite > > resolution, but failing to terminate a for loop correctly with a precision > > of 1/25 that's OUCH. > > > > Did I misunderstand something? I was under the impression that the loop indeed terminates, it’s just sometimes missing the last step since we use equality comparison. > > In retrospect, we should perhaps have excluded the max range item from the range, but that’s a tough one to revert now due to the amount of existing designs using this. > > -Marius > > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org (mailto:Discuss@lists.openscad.org) > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > >
R
runsun
Thu, Nov 19, 2015 8:43 PM

vicnet wrote

Thanks all.
I change iterator to integer and add a 'let' function to calculate the
parameter by dividing.
a+
Vicnet

Check out this recent thread for why iterating over a floating point
increment is not reliable:

Simple addition of numbers introduces error
http://forum.openscad.org/Simple-addition-of-numbers-introduces-error-td14408.html


$  Runsun Pan, PhD

$ libs:

doctest ,

faces ( git ),

offline doc ( git ),

runscad.py( 1 , 2 , git );

$ tips:

hash( 1 , 2 ),

sweep ,

var( 1 , 2 ),

lerp ,

animGif ,

precision

--
View this message in context: http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642p14655.html
Sent from the OpenSCAD mailing list archive at Nabble.com.

vicnet wrote > Thanks all. > I change iterator to integer and add a 'let' function to calculate the > parameter by dividing. > a+ > Vicnet Check out this recent thread for why iterating over a floating point increment is not reliable: Simple addition of numbers introduces error <http://forum.openscad.org/Simple-addition-of-numbers-introduces-error-td14408.html> ----- $ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py( 1 , 2 , git ); $ tips: hash( 1 , 2 ), sweep , var( 1 , 2 ), lerp , animGif , precision -- View this message in context: http://forum.openscad.org/Loop-precision-floating-point-precision-tp14642p14655.html Sent from the OpenSCAD mailing list archive at Nabble.com.