-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make UnitVec initializing constructor public #575
Comments
Now I'm also wondering in general if this function ( |
Hi @opera-aberglund ! The constructor you're referencing is what I've been calling an "initializing" constructor; not the default constructor. What I've been calling the "default" constructor is the constructor that can take no arguments and is currently declared as: constexpr UnitVec() noexcept = default; So, I changed the issue's title to match the names I'm using. Incidentally, cppreference.com states: "A default constructor is a constructor which can be called with no arguments". |
The function interface it seems like you want is one which allows you, the user, to set the X and Y elements of the 2-D unit vector. Problem is, that only values of X and Y, whose magnitude of the line whose representative point is that X and Y and the origin of 0 and 0 is 1, are valid values. That's the invariant of the class. It's essentially also the invariant of the Now I see that this invariant is not explicitly stated. As a matter of practice/convention, it should be stated as a Doxygen-like comment with the So allowing the user to directly enter the X and Y values of the Is the |
The Across different hardware however, it's possible that it might generate slightly different values. That's true of all IEEE-754 floating point math operations - all of them. Even setting a floating point value like 0.3. Now I'm thinking that this is what you're running into. That's not technically "non-determinism" but is often never-the-less referred to as such. Setting X and Y directly wouldn't solve this problem. While it might mitigate it, it will do nothing to deal with The only absolute solution for this is to require either:
|
So what was happening was that I was working with serialization and deserialization again and found that the value of a unit vector wasn't exactly the same as the one it was deserialized from so the equality check failed. What I was doing was to break down the unit vector into an array of two floats before serializing, and on deserialization I tried to reconstruct the init vector by calling So what I was expecting to happen was if I entered a valid unit vector as arguments to I can try to reproduce it and show some exact values where it happened for me. EDIT: This also happened on the same machine when I was testing, so it was exactly the same hardware running the calculations. |
Oh, that's a relief to me. That's a more practically solvable problem IMO. So the gist is: |
As an initial concept/idea, this may be solvable splitting
For serialization/deserialization then, could you use the slower path? I'd guess that'd be okay since I wouldn't imagine that'd be in as tight a loop of your code. |
I think a slower would be fine, there are not that many calls to it, mainly just for a few of the joints that have internal unit vectors that needs to be serialized. |
I've written some unit test code now that checks the reversibility of the Turns out that for When you're comparing for "exactness", I suspect you're using So for example, the include file The significance of this is that the error introduced from serialization/deserialization should also be constrained within those ULPs as well. Assuming the serialization/deserialization library you're using doesn't itself introduce imprecision. Can your code live with 1 or 2 ULPs of imprecision? I don't know the answer to that but I'll help however I can. Speaking of which, I'll try to merge the unit test code addition in so you can see the actual code I'm using to test this. |
That's interesting! I just ran another test and here's an example of what I got: Initial value (from the I guess this is within 2 and 3 ULPs? Interestingly, calling The serialization library (Cista) I'm using doesn't itself introduce any imprecision. That's a valid question though about the significance, and it's hard to tell if this can lead to de-sync issues in practice or not, but I'd like to minimize the risk as much as possible of course. I'm currently using my own fork of PlayRho where I've made the initialization constructor public just so I can use it in deserialization. What is your opinion on this? I totally understand why it's private right now because you want to protect the invariant, but what if my serialization library could be considered a "friend" class that has special access? I'm hoping I can get it open sourced soon as well! Would be really nice to get some more feedback from you on how I handle the objects, and perhaps we can work more closely together in the future. |
I think there's a space for an interface behavior change that's more apt to take the values without change. I've reopened this issue to try and support that and let you know more when that's available. |
Ideally IMO, |
@opera-aberglund PR #577 provides a constructor a bit like you were asking for. It expects the value as a A difference from what you've mentioned is that the original constructor, that does no checks and has that's been private, now - internally - requires the passage of the object of an internal type (let's call this "tagged dispatch") that only member functions can use. |
That sounds exciting!
Happy to provide feedback. Not certain about what you'd like to get feedback on though. Do you mean on the discussion (under Discussions) on maintaining the same object IDs? Or something else like on the code you're hoping to open source?
I'd be happy to do that also.
A way I like determining that is by using There's other ways of doing it too of course, like type punning methods. I'm not as fond of those however. I suspect for more than an ULP or two, the union-using difference type punning method would have to be faster than iteratively calling |
Assuming the merged changes suffice. Closing this issue then. Please re-open this if it doesn't help enough or open a new issue. |
Hello, it's me again!
I noticed I was having some trouble with my unit vectors not having the exact values I wanted after I had created them with
UnitVec::Get
. I assume there must be some floating point errors in the calculations in this function?I thought it would be nice to just make the initializing constructor public
but perhaps add some assertions that the values are between 0 and 1?
The text was updated successfully, but these errors were encountered: