I was writing something up about floating point error and its effect on
ranges.
As you might know, the theoretical problem is that fractional arithmetic
is imprecise and so in a range like [ 0 : 0.1 : 1 ], the system might
calculate that there are 9.999999999999999 steps, say "oops, sorry,
can't quite fit that last one", and give you 0.0, 0.1, ... 0.9, never
reaching 1.
But I haven't been able to find an example of such a range, one where
OpenSCAD generates something other than the "obvious" answer. In
particular, I haven't found a range that has the "wrong" number of results.
Can anybody find a range that doesn't have the obvious number of results?
Heck, I haven't been able to find one that doesn't end precisely at
the end point. Can anybody find one of those?
On Sat, Aug 13, 2022 at 10:11:57AM -0700, Jordan Brown wrote:
I was writing something up about floating point error and its effect on
ranges.
As you might know, the theoretical problem is that fractional arithmetic
is imprecise and so in a range like [ 0 : 0.1 : 1 ], the system might
calculate that there are 9.999999999999999 steps, say "oops, sorry,
can't quite fit that last one", and give you 0.0, 0.1, ... 0.9, never
reaching 1.
But I haven't been able to find an example of such a range, one where
OpenSCAD generates something other than the "obvious" answer. In
particular, I haven't found a range that has the "wrong" number of results.
Can anybody find a range that doesn't have the obvious number of results?
Try loops of 7 iterations of 1/5th and 1/6th.
I made a spreadsheet that has 1, 2, 3... across the top,
1, 2, 3 ... down a column and in the grid I calculate
(1/column_header) * row_header - (row_header/column_header)
(I put the first half in a cell by itself to prevent the spreadsheet
from getting too smart).
the column 5 and 6, row 7 results differ in signedness. (and neither
is zero).
Roger.
--
** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110 **
** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 **
f equals m times a. When your f is steady, and your m is going down
your a is going up. -- Chris Hadfield about flying up the space shuttle.
On 13.08.22 19:11, Jordan Brown wrote:
Heck, I haven't been able to find one that doesn't end precisely
at the end point. Can anybody find one of those?
That is very intentional by means of this PR:
https://github.com/openscad/openscad/pull/3253
ciao,
Torsten.
On 8/13/2022 11:20 AM, Torsten Paul wrote:
On 13.08.22 19:11, Jordan Brown wrote:
Heck, I haven't been able to find one that doesn't end precisely
at the end point. Can anybody find one of those?
That is very intentional by means of this PR:
Yes, I noticed that it does base+i*step rather than accumulating, and I
agree that that's a big win.
But even then, there's no guarantee that 0.1 * 10 will exactly equal 1.
I just tried what I should have tried first, which is brute force
checking numbers looking for ones where 1/n*n does not equal 1. The
first one I found was 49. (After that, 98, 103, 107, 161, 187,196, 197.)
And indeed:
a = [for (i=[ 0: 1/49 : 1 ]) i];
echo(a=a);
echo(len=len(a));
echo(last=a[len(a)-1]);
echo(equal=a[len(a)-1]==1);
echo(diff=a[len(a)-1]-1);
ECHO: a = [0, 0.0204082, [blah blah blah], 0.979592, 1]ECHO: len =
50ECHO: last = 1ECHO: equal = falseECHO: diff = -1.11022e-16
On 8/13/2022 10:47 AM, Rogier Wolff wrote:
Try loops of 7 iterations of 1/5th and 1/6th.
I don't understand what you mean, in terms of an OpenSCAD range
specification.
I tried brute forcing the first challenge (getting the wrong number of
results), and for [ 0 : 1/n : 1 ], for n from 1 to 1000, I always got
the right number of entries (n+1). But of course that doesn't mean that
some other random combination wouldn't be a problem.
echo("start");
for (n=[1:1000]) {
a = [ for (i=[0:1/n:1]) i];
if (len(a) != n+1) {
echo(n);
}
}
echo("end");
yields nothing.
On Sat, Aug 13, 2022 at 5:44 PM Jordan Brown openscad@jordan.maileater.net
wrote:
On 8/13/2022 11:20 AM, Torsten Paul wrote:
On 13.08.22 19:11, Jordan Brown wrote:
Heck, I haven't been able to find one that doesn't end precisely
at the end point. Can anybody find one of those?
That is very intentional by means of this PR:
https://github.com/openscad/openscad/pull/3253
Yes, I noticed that it does base+i*step rather than accumulating, and I
agree that that's a big win.
Hello I'm the author of that change, and there's another trick done in that
PR to help ensure the correct number of values is always reached.
You can see in the lines of code highlighted here:
https://github.com/openscad/openscad/pull/3253/files#diff-2ecb150c1fd88f2ab8aaf603ccee701e18d113bdb931ffc3aa4eb528243a3a38R1028-R1031
It pre-calculates the total number of values in the range, based on: (end
But even then, there's no guarantee that 0.1 * 10 will exactly equal 1.
I just tried what I should have tried first, which is brute force checking
numbers looking for ones where 1/n*n does not equal 1. The first one I
found was 49. (After that, 98, 103, 107, 161, 187,196, 197.)
Hmm, this is true that the final value is not necessarily equivalent to the
end of the range, I hadn't thought much about it at the time.
But also consider that the step might not always be a perfect divisor for
the span of (start - end) (e.g [0 : 3/4 : 2 ] )
so it wouldn't be as simple as checking if we're on the final value and
just spitting out the given end_value.
Maybe we could do some more trickery with std::nextafter and/or similar
functions to check if the calculated final value is just nearly equal to
the given end value, and swap it out if true.
On Sat, Aug 13, 2022 at 04:19:20PM -0700, Jordan Brown wrote:
On 8/13/2022 10:47 AM, Rogier Wolff wrote:
Try loops of 7 iterations of 1/5th and 1/6th.
I don't understand what you mean, in terms of an OpenSCAD range
specification.
As I said: I did it in a spreadsheet. From the spreadsheet I was able
to find two candidates of which one surely would go wrong if openscad
would do it the obvious way. But we now know that in 2020 someone made
openscad to it the non-obvious way eliminating this problem.
Roger.
--
** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110 **
** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 **
f equals m times a. When your f is steady, and your m is going down
your a is going up. -- Chris Hadfield about flying up the space shuttle.
On 8/13/2022 5:50 PM, Hans L wrote:
Hello I'm the author of that change, and there's another trick done in
that PR to help ensure the correct number of values is always reached.
Yes, I saw that. I don't know enough low-level floating point math to
have an opinion on whether it's sufficient. The fact that I wasn't able
to demonstrate a problem is certainly encouraging.
Hmm, this is true that the final value is not necessarily equivalent
to the end of the range, I hadn't thought much about it at the time.
Most of the time, it shouldn't matter.
But also consider that the step might not always be a perfect divisor
for the span of (start - end) (e.g [0 : 3/4 : 2 ] )
so it wouldn't be as simple as checking if we're on the final value
and just spitting out the given end_value.
Yep.
Maybe we could do some more trickery with std::nextafter and/or
similar functions to check if the calculated final value is just
nearly equal to the given end value, and swap it out if true.
Whether it matters will depend on what the caller does with the values.
It might not be just the end point that matters - there is similarly no
guarantee that intermediate values that mathematically should hit
particular values will hit exactly those values. (E.g., in [0 : 1/49 :
2], the middle value should be exactly 1, and isn't.)
If it was considered to be an issue, probably the rightest answer would
be to have all equality checks be very slightly fuzzy.
Until somebody can demonstrate a practical problem, I wouldn't worry
about it.
Let me be clear: I'm not complaining. Tiny errors are inherent in
finite-precision arithmetic. I was pleasantly surprised that I wasn't
able to find more of them. I was about to advise somebody to use
integer steps in the "for" and scale to the desired value in later
processing[*], because rumors said that was better, but when I wasn't
able to demonstrate any problems I had to rethink that advice.
[*] That is, something like "for (i = [0:n]) { val = i*max/n; ... }"
instead of "for (val = [0:max/n:max]) ...".
If nobody can demonstrate a case where it doesn't get the obvious number
of results, I don't see any reason for that kind of advice. (It still
sometimes leads to more obvious expressions, but that's a different
rationale.) Hence the challenge, to see if anybody could find such a
problematic case.
Let me be clear: I'm not complaining. Tiny errors are inherent in
finite-precision arithmetic. I was pleasantly surprised that I wasn't able
to find more of them. I was about to advise somebody to use integer steps
in the "for" and scale to the desired value in later processing[*], because
rumors said that was better, but when I wasn't able to demonstrate any
problems I had to rethink that advice.
I still do that, but I'm old school. Heck, I once programmed in FORTRAN IV.
Would it be feasible to implement something like the fraction module for Python in OpenSCAD?
https://docs.python.org/3/library/fractions.html
(I think that's the right link --- it was mentioned in a discussion of a text interface where splitting the screen was an option and the developer found that using this module allowed splits to occur as expected)
William