-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
fix spinner precision #13078
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 spinner precision #13078
Conversation
Codecov Report
@@ Coverage Diff @@
## branch-3.4 #13078 +/- ##
===========================================
Coverage 92.62% 92.62%
===========================================
Files 321 321
Lines 20383 20383
===========================================
Hits 18880 18880
Misses 1503 1503 |
Hi @msalem99 quick note: the selenium tests under For testing BokehJS changes, there are pure JS integration tests here nowadays: https://github.com/bokeh/bokeh/tree/branch-3.2/bokehjs/test/integration That's going to be the appropriate place to add tests for this. @mattpap can offer specific guidance if needed. |
@@ -104,9 +105,9 @@ export class SpinnerView extends NumericInputView { | |||
} | |||
|
|||
get precision(): number { | |||
const {low, high, step} = this.model | |||
const {low, high, step, initial_value_precision} = this.model |
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.
Do we actually need to save and and only use the precision from the initial value? Would it not simply work to just always consider the precision of the current value? (if the initial value is a float then adding an int to it should not change its precision, for not-extremely large values at least, and if we are in that regime we won't be able to honor the step anyway)
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.
If I understand you correctly, you mean to apply the precision on this.model.value directly? if so then I actually did do that in the beginning (see first commit a0b749a) but that meant floating errors in some cases, for instance when decrementing, the value would end up showing as 1.299999999998 or so. Please correct me if I am wrong
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.
Ex: Spinner(value=1.3, low=0, high=10, mode="float", step=1)
increment once: value = 2.3
decrement once: value = 1.2999999999999998
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.
Properties are meant for configurability or for computed values of significance to the user, so if that's not the case then this shouldn't be a public property, and possibly a property altogether. If we really need to store this, then a view private member should be sufficient.
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.
If it isn't stored do you suggest doing this another way? As I mentioned , taking the value's precision right away resulted in large precisions, or am I doing something wrong?
Thank you for your feedback
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.
However, I'm not sure if we actually need all of this complexity. What's the point of handling precision in this widget, trying to hide the fact that it uses floating point numbers? If looks are the goal, then one can use a formatter for this. If one actually wants non-fp behavior, then we should provide appropriate number types, which then could be reused in other widgets to get equivalent behavior.
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 would definitely be more comfortable with computing an initial (internal) "max precision" if that value fed into a formatter, rather than actually futzing with values.
Unless we need baselines for this, in particular image baselines, then |
Hello, |
I tried to implement a max_precision but after writing unit tests in bokehjs/test/unit/models/widgets/spinner.ts it seems that sometimes the initial value entered has a precision of 16 so fp issues emerge (this can be seen in the failing tests in 0452e44) even from the value inputted by the user. I am not sure why the initial inputted value has a precision of 16 so any feedback is appreciated. |
Well, if the initial value is not an exact fp value that seems about right. I am really tempted to more or less punt on this:
As @mattpap suggests, we are more or less trying to magic away the intrinsic behavior of floating point values, and that is just a hopeless task. cc @bokeh/dev |
I figured maybe I could obtain the max precision through the format_value string by counting the decimals after the dot from the string, this would work but I think it would mean that using the format property for approximating values would be useless since the values are truncated. Not sure if this is considered clean enough. Any feedback would be appreciated. |
@msalem99 the more I think and look at this, the more I think perhaps we should simply abandon the precision complexity altogether. Doing a quick survey online:
I don't think there is any standardization of consistency at all in this space, and every option seems to have some drawback or other. IIRC all the precision computations came about as a result of some problem with rounding. But we were only rounding because there was no option to add a formatter at that time in the early days. I think now that a format can be supplied, the actual best thing to do is:
Then the only possible "problem" is that floating point precision may cause the min or max values to be "missed" but that's just how fp works. I think we can just reiterate that fact of life in the docs, and not worry about it. |
Thank you for your feedback, I think this is definitely the most suitable course of action, I was hoping I could implement the max_precision correctly to use it as a default format if a format is not given but I am not sure how to do that since the precision function that returns max is essentially useless since the step could also be a floating point, same goes for the high and low. any suggestion regarding a default format? or just leave it as fp incase no format is provided? Thank you |
I agree with making it simpler, but I worry if floating point precision issues could cause the value to exceed the upper bound by even the smallest amount. E. g. if the value is used to compute an index into an array, now there might be out of bounds access, despite the spinner bounds seemingly being safe against that. Send like that could lead to some dangerous apps! |
@jbednar by "miss" the max/min values, I meant "might never be able to attain those values" (even if in a "perfect math sense" they should be able to). I agree that a strict comparison against min/max is always needed to prevent any overshoot or undershoot due to fp precision loss. |
@msalem99 I think improvements to default formatting is a perfectly reasonable thing to look at but let's save it for a follow-on issue separate from this one |
I am not sure where to go from here regarding this fix. Also I am also not sure why the Bokeh-CI macos unit-tests and windows minimal-deps are failing, any feedback is appreciated. Thank you. |
@msalem99 I don't think the failures are related to your PR. The documentation job is failing everywhere due to a recent downstream package update. I think the others were conda env network failures. I've restarted all the failed jobs. I am going to look over the PR (especially tests) tomorrow but I think it should be close and/or ready |
expect(input.value).to.be.equal("4.36") | ||
|
||
await mousedown_mouseup(increment_button) | ||
expect(input.value).to.be.equal("5.00") |
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.
Is this the desired behaviour? I think I might have expected that in this case the increment button simply had no effect at all. Perhaps it should be configurable whether a "partial step" is allowed at the bounds (to exactly attain the bounds) or whether the step is strictly respected at all times (even if it means the bounds values can never be attained)
Same questions for lower bound, of course.
cc @bokeh/dev
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.
in-case of the presence of a reach_bounds property and set to true, if a partial step is made to reach the bound exactly, should this partial step be preserved in the followed opposite step and then back to the original step? or should the original step be applied right away?
ex:
high: 5 step:0.5 value 4.7
increment -> 5
2x decrement -> 4.7 , 4.2 or 4.5 , 4.0?
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.
@bokeh/dev thoughts on a preference? It could be made configurable I suppose, but I'd rather just pick a good default to start and only add a configuration if it becomes needed.
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.
Hi @msalem99 apologies for the delay, I have not had as much time for Bokeh in the last few months. I'd like to get this merged for 3.3 so that we can also add the formatter option along with it. Can you fix the small merge conflict, and submit an issue regarding adding a formatting property to the spinner?
Co-authored-by: Bryan Van de Ven <bryan@bokeh.org>
Hello @bryevdv, |
Thanks for you patience @msalem99 the tests all look good to me so i will go ahead and merge this now. |
* Added value precision check when obtaining the precision * Improved the initial fix and Added tests * Added initial_value_precision to the Python Spinner module * Added internal max precision member in the spinner module * Added unit tests for spinner widget * obtain initial precision of initial value from the inputted num string * Removed all precision computation from Spinner model in bokehjs * override reach_bounds in spinner * Update bokehjs/src/lib/models/widgets/spinner.ts Co-authored-by: Bryan Van de Ven <bryan@bokeh.org> * fix linting errors --------- Co-authored-by: Bryan Van de Ven <bryan@bokeh.org>
* Added value precision check when obtaining the precision * Improved the initial fix and Added tests * Added initial_value_precision to the Python Spinner module * Added internal max precision member in the spinner module * Added unit tests for spinner widget * obtain initial precision of initial value from the inputted num string * Removed all precision computation from Spinner model in bokehjs * override reach_bounds in spinner * Update bokehjs/src/lib/models/widgets/spinner.ts Co-authored-by: Bryan Van de Ven <bryan@bokeh.org> * fix linting errors --------- Co-authored-by: Bryan Van de Ven <bryan@bokeh.org>
* Added value precision check when obtaining the precision * Improved the initial fix and Added tests * Added initial_value_precision to the Python Spinner module * Added internal max precision member in the spinner module * Added unit tests for spinner widget * obtain initial precision of initial value from the inputted num string * Removed all precision computation from Spinner model in bokehjs * override reach_bounds in spinner * Update bokehjs/src/lib/models/widgets/spinner.ts Co-authored-by: Bryan Van de Ven <bryan@bokeh.org> * fix linting errors --------- Co-authored-by: Bryan Van de Ven <bryan@bokeh.org>
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
It seems that when the precision of the value is larger than the precision of the step ex value=1.333 step=1.22, the value will be truncated when incremented or decremented due to the precision of the step applying, I proceeded to save the initial value precision and took that value into consideration when calculating the precision of the incremented/decremented value.
tests have been added that test in the case of the precision of the value is larger than the step or vice versa
Please provide any feedback if things should be done differently. I am aware that setting the initial_precision_value in the render may not be best, can this be done in a better way?