Mesh.Vertices accuracy seems wrong, why?

OK, but what I don’t understand is how coercing that point can add more data?
My understanding must be missing something here… Maybe how floating points work.

Does it make sense to you that the vertex returns x.xxxxx , y.yyyyy , z.zzzzz and that this once coerced into a 3d point is not x.xxxxx00000 , y.yyyyy00000 , z.zzzzz00000 ?

As mentioned multiple times on this forum don’t assume bit-for-bit parity when working with floating point numbers (single or duouble precision).

Further, Python does its own things to numbers when printing, so don’t rely on that either.

What may make sense to you as a normal human being does no longer hold in the realm of floating point computation and even just floating point representation in computer memory.

You can use the EpsilonEquals() utility functions provided by Rhino.RhinoMath:

https://developer.rhino3d.com/api/RhinoCommon/html/T_Rhino_RhinoMath.html

Ouch, you are right, that hurts my normal way of thinking and calculating numbers… don’t know if I want to tumble down that rabbit hole… :smiley:

But I still don’t understand why coercing the vertex doesn’t return the same number as just pringing the vertex value… why are they handled differently? Why is the vertex rounded when presented to me?

Here some more info on Python side of things - may be a bit different for IronPython, but probably holds for the most part:

https://docs.python.org/3/tutorial/floatingpoint.html

I recorded RH-58628 to keep track of this problem.

Right now, users can use the mesh.Vertices.GetPoint3dAt() function to look at the double precision vertex of the mesh. This is however a little less easy to find than the indexer [], which returns the single precision.

3 Likes

From the Python floating point tutorial perhaps my favourite bit is this:

print ( 0.1 + 0.1 + 0.1 == 0.3)

Just paste that in EditPythonEditor and run it :slight_smile:

4 Likes

image

Ok, I am logging off and will stare at the sky for a while.
My world just crumbled.

1 Like

Welcome to the wondrous world of programming!

3 Likes

OK so I guess that with my new “knowledge” it refines to a wish:
I wish that the coerced 3D point of the single precision indexed point results in the same value as the printed value of that given point.
(Is that accurately stated? I feel I know nothing now… :wink: )

Printing single precision, as you can see, has less accuracy than double precision.
We can’t have the indexer return doubles now, because it would break existing plug-ins. And we are trying to keep new versions of RhinoCommon compatible with older plug-ins.

We could deprecate the indexer, and print a warning that points to this or a similar discussion. It could also suggest the GetPoint3dAt method. Would that be enough?

:grimacing: I don’t know anyting any more… Nathan just broke my mind…

A = (0.1+0.1+0.1)
B = 0.3
print A == B

It’s False… Hahahahahaaa…

Give it a few days :slight_smile:

2 Likes

Maybe that is what happens to the binary value.
And then the same (binary) number is simply formatted as decimal using different precisions …
… I guess since one is a 32 bit value and the other a 64 bit value.

EDIT:

EDIT:

You may want to use the “r” (roundtrip) format specifier:
https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings

1 Like

After 20 years of math,geometry programming I ended up to think geometry like what happens in the real world, a point a line and a surface do not exist. They are balls, cylinders and skins with a epsilon radius/thickness. This helps to make robust coding (at least I hope)
Gd

This still baffles my mind.
4 years in and I still don’t understand, but it now makes sense why rockets fall out of the sky every now and then… Since computer logic is so different from human logic.

Proposal:
Would it be possible to add a decimal limiter for all calculations in python so it never uses more than 15 decimals? Or use the accuracy of the Rhino document, without having to use round() on every thing we check?
From this simple example it seems like 15 decimals is the turning point.

A = (0.1+0.1+0.1)
B = 0.3

print "A=",A," B=",B
if A == B:
    print "A = B"
else:
    print "they are not the same"
    print "the difference is", A-B

for decimals in range(13,20):
    if round(A,decimals) == round(B,decimals):
        print "With ",decimals,"decimals they are the same"
    else:
        print "They are NOT the same, with ",decimals, "The difference is",round(A,decimals)-round(B,decimals)

A= 0.3 B= 0.3
they are not the same
the difference is 5.55111512313e-17
With 13 decimals they are the same
With 14 decimals they are the same
With 15 decimals they are the same
They are NOT the same, with 16 The difference is 1.11022302463e-16
They are NOT the same, with 17 The difference is 5.55111512313e-17
They are NOT the same, with 18 The difference is 5.55111512313e-17
They are NOT the same, with 19 The difference is 5.55111512313e-17

It is not possible to do that on the operator level, or at least not smart.

Instead you’ll have to change your logic for comparison.

And because of these imprecisions we have equality checks that allow you to specify a precision you know will work for your cases. In RhinoCommon we have under Rhino.RhinoMath.EpsilonEquals(a, b, epsilon) where epsilon would be the term specifying the precision. For instance epsilon = 0.000001.

1 Like

Do you happen to know whether RhinoPython Floats are single or double precision? I read on the internet that most Pythons use doubles, but Holo’s example looks more like singles. On the other hand I’m not sufficiently expert to know.

Yeah, that makes sense from the computer-math perspective, it just doesn’t make sense from a people-math perspective. I find it really strange that this human-computer-human conversion is allowed to end up with false results. After all computers are here to assist us, not cause issues for us :slight_smile:
Simple math should in other words just work according to our ten digit rules. Baffling, but understandable, yet my user-oriented-designer brain just can’t let this one pass.

Take a look at this example, it ONLY works when A, B or C equals 0.3. Now how is that logical for a new user?

import random

limit = 10000
n = 0
target = 0.3

while True:
    n += 1
    if n> limit: break
    A = round(random.random(),1)
    B = round(random.random(),1)
    C = round(random.random(),1)
    if (  A + B + C ) == target:
        print "It took ",n,"iterations to get the target",target
        print "A=",A," B=",B," C=",C
        break

BUT if I change the target to 0.4 then it handles all alternatives (0.1+0.1+0.2 or 0.2+0.2 or 0.1+0.3 or 0.4)
Go figure…

I think you’ll have to take this one up with processor/hardware designers. And maybe after that with programming language creators.