Conversation
Just to be clear, this bug exists in our outdated version of d3-format. On newer versions, this bug is gone: https://runkit.com/embed/ooxgp8bijh5w |
|
Thanks for this PR @archmoj ! It already improves things quite a bit. Do you think it would be a lot of work to switch to the newer |
Thanks for the note. But that one still returns extra zeros! |
Good question. However; I think we should try that on a separate branch/PR. |
You're right! Sorry for the misinformation.
Sounds good. Well, if upgrading |
|
Ok, thank you @archmoj! I think I'll let @etpinard or @alexcjohnson review this one. |
| // Note 1: | ||
| // 0.3 != 0.1 + 0.2 but 0.3 == ((10 * 0.1) + (10 * 0.2)) / 10 | ||
| // Attempt to use integer steps to increment | ||
| var magic = 1 / dtick; |
There was a problem hiding this comment.
Do you have a reference explaining that "magic"?
There was a problem hiding this comment.
Unfortunately no. But the trick appear to be working.
There was a problem hiding this comment.
Hmm, do we really need this block for tickformat values other than 'p'?
There was a problem hiding this comment.
Yes it is also required e.g. with s.
Please find the new mock added in 740574b which currently renders as:

There was a problem hiding this comment.
Yes it is also required e.g. with
s.
Ok so could then have
if(isNumeric(dtick) && (tickformat.indexOf('s') !== -1 || tickformat.indexOf('p') !== -1)) {
// your new logic
}that would make this patch a lot less stressful to merge.
There was a problem hiding this comment.
@antoinerg @etpinard the logic is now moved to Lib in 916bf80 and I improved test coverage in 86b135d.
| var lenX0 = ('' + x).length; | ||
| var lenX1 = ('' + newX).length; | ||
|
|
||
| if(lenX1 > lenX0 + lenDt) { // this is likey a rounding error! |
There was a problem hiding this comment.
@archmoj Just a thought here: if you know the number of decimals in dtick, you also know that the number of decimals in the final number should never exceed it. So if newX.split('.')[1].length > dtickDecimalLength then you can round with a precision of dtickDecimalLength. I have the feeling it could fix the 55% but I may be wrong.
There was a problem hiding this comment.
@antoinerg thanks for sharing this.
That one not only depends on the dtick but also on the previous tick value. For example if we start from 1000.0001 and dtick=1, the next tick should be at 1001.001.
Also I should clarify that the case of 55% is a d3 bug which is separated from what is going to be fixed in this PR i.e. providing better raw tick values.
There was a problem hiding this comment.
That one not only depends on the
dtickbut also on the previous tick value. For example if we start from 1000.0001 anddtick=1, the next tick should be at1001.001.
Wouldn't taking the maximal number of decimals in either dtick or the start number work?
force magic number to be positive to avoid getting -0
|
|
||
| module.exports = function incrementNumeric(x, delta) { | ||
| if(!delta) return x; | ||
|
|
There was a problem hiding this comment.
One may simply return x + delta here to find out various failing tests.
| newX = +parseFloat(newX).toPrecision(12); | ||
| } | ||
|
|
||
| return newX; |
There was a problem hiding this comment.
Thanks for adding those tests @archmoj - I understand (a little bit) more what you're attempting.
That said, I feel like we should be extending numFormat:
plotly.js/src/plots/cartesian/axes.js
Lines 1311 to 1408 in 96fe262
where instead of exiting early
plotly.js/src/plots/cartesian/axes.js
Line 1338 in 96fe262
for when tickformat is set, we could construct the formatted number ourselves for tickformat values that include s and p. To me, that sounds a lot more robust than these floating-point tricks that gets us a rounded-off tick step. What do you think?
There was a problem hiding this comment.
it would be interesting to fix the issue at the root cause
What is the root cause in your mind here?
|
@archmoj I did a little testing, and I think you may be on to something useful. AFAICT, as long as we add/subtract integers, and then divide by a power of 10 (again, explicitly rounded to be an integer), we're protected from these floating point errors over a quite wide range of values. (I tested For performance purposes I suspect we'd want to calculate up front the power of 10 that makes both Now, the question is what cases that actually fixes? We could use it to fix cases we don't handle correctly right now in our own formatting - for example But it won't generally fix Really the only solution I see is to encourage users never to use bare |
@alexcjohnson Thanks very much for testing this. |
Good point. Most of these test would fail with the master version: https://github.com/plotly/plotly.js/blob/76b036859248c43518bd4f15f5dfa35444f665ed/test/jasmine/tests/lib_increment_numeric_test.js @alexcjohnson Thank for mentioning 'e' & 'g' as well. |
|
In order to potentially get this PR merged:
|
This case is fixed in d3 > v3. |
|
@archmoj I stumbled upon https://github.com/MikeMcl/decimal.js/ - which might be of interest for this PR. |
|
Reworked in #5114. |


Fixes #3814.
This PR removes some precision errors happening during tick increments on axes.
Two patches are added here:
First and most importantly we use a dynamic pattern similar to (10 * 0.1 + 10 * 0.2) / 10 to get exactly
0.3as the result not0.30000000000000004.Then we check if the length of new x (i.e. x0 + dtick) as an string is greater than the total lengths of previous value and
dtickwe round the result to 12 digits. This helps cover few edge cases e.g. where 3 * 0.3 sums up to0.8999999999999999not0.9.To view before vs after changes please view 4855033?short_path=4bd0c49#diff-4bd0c497a76299dd674f7b3092fb6aad.
Also to mention it appears that there is currently a bug in d3 formatting where 0.55 in
.
pdoes not return a rounded 55%.@plotly/plotly_js