-
-
Notifications
You must be signed in to change notification settings - Fork 23.3k
Fix snapping logic in Range #109100
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
Fix snapping logic in Range #109100
Conversation
For me, this behavior is expected and very natural. The minimum value also acts as an offset and gives a lot more control on the range values. With this change, how to specify a range of some odd numbers for example? |
@ze2j Updated the PR to restore that functionality, and document it. To my surprise... this is actually still way more precise than in master, even though at a glance it looks like it's functionally the same. It's actually a toss-up which is more precise, in some cases it's actually more precise to do it this way, somehow. The note about eventually switching to 128-bit floats is still the case, though, as it's still wrong for some values as it is now. |
The accuracy difference may be explained by the This PR has also the added benefit to be more predictable than the master one as the |
Does this improve the serialization situation reported in #107095? |
@clayjohn Yes. |
We could either close it, or consider that this PR demotes it to no longer being a release blocker. Either way works for me. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like it's an improvement over master
. Code looks good to me.
My 2c, I'd say this keeps #107095 at "Bad" as a release blocker, since it doesn't fully fix it.
Ok... but what about fixing this further? Unfortunately, it's not really possible to do that.
The only solution I can think of to fix this further would be to use 128-bit floats instead of 64-bit floats, store the step size in 128-bit, do the math in 128-bit, and then store the computed value as 64-bit.
There's a better solution: Store step_size
as a fixed-point number. This should allow us to store the exact value the user entered in UI (e.g. 0.01
), instead of a floating point approximation, and snap the values for storage (e.g. FixedPoint snap(double val, FixedPoint snap)
) accurately as well. I think this is more appropriate than a 128 bit float.
This does not require c++20, but it does require quite a lot more implementation work. So it may not be an option for 4.5.
f5c19d5
to
eee7ce6
Compare
@Ivorforce I forgot about fixed point, good idea! Godot actually already has |
c029638
to
de83570
Compare
de83570
to
af8bdac
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy to see that fixed points solve the issue for good! :)
The new version looks kinda slow. I suppose it's not used in any performance critical operations (just UI)?
Range is UI-only. Aside from editing numeric properties, it's used for things like progress bars, but nothing really performance critical. |
Thanks! |
Changes to Range turned out to be risky, so I'm un-marking this as cherry-pick-able. |
This PR is a fix for issue #107095. Fixes #107095.
This PR changes Range's step feature to use
r128.h
instead of the existing algorithm.In master:
With this PR: everything works as expected.
Old PR description:
This PR is a partial fix for issue #107095, but not a full fix.
This PR changes Range's step feature to use
Math::snapped
instead of this custom algorithm.With this PR:
The new results with this PR are approximately 99.9995% closer to the desired value, and in some cases, match exactly.
Ok... but what about fixing this further? Unfortunately, it's not really possible to do that. The
step
value we're plugging intoMath::snapped
simply doesn't have enough precision for this. When a property hint has a step size of0.001
, that gets converted into the nearest possible float, which is technically0.00100000000000000002081668171172
. And, the number0.1
is technically not a multiple of that value, even though it is a multiple of the decimal0.001
.The only solution I can think of to fix this further would be to use 128-bit floats instead of 64-bit floats, store the step size in 128-bit, do the math in 128-bit, and then store the computed value as 64-bit. If we were using C++23 or later, we could switch to
std::float128_t
. Without C++23, the best we could do is platform-specific compiler extensions or a library. My recommendation is, for now, merge the 99.9995% fix in this PR while Godot uses C++17, and then we can switch the step size to 128-bit in the future when Godot requires C++23.