Skip to content
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

Don’t format -0 with a sign. #14

Closed
mbostock opened this issue Nov 6, 2015 · 3 comments
Closed

Don’t format -0 with a sign. #14

mbostock opened this issue Nov 6, 2015 · 3 comments
Assignees

Comments

@mbostock
Copy link
Member

mbostock commented Nov 6, 2015

If the input value is exactly -0, or if when rounded according to the format’s precision is equivalent to -0, we should treat it as exactly 0 rather than -0.

This is necessary because tick values can be slightly off due to floating point precision, so a value that is intended to be exactly 0 may instead be something like -1.7763568394002506e-16. I can’t seem to reproduce this in 4.0’s d3-scale, but it’s definitely the case in 3.x:

var y = d3.scale.linear().domain([1.575008840033951, -0.7414613146919857]);
y.ticks(10);

Returns:

[
  -0.6000000000000001,
  -0.4000000000000001,
  -0.2000000000000001,
  -8.881784197001253e-17,
  0.1999999999999999,
  0.3999999999999999,
  0.5999999999999999,
  0.7999999999999999,
  1,
  1.2,
  1.4
]

Related d3/d3@4946a8c.

@mbostock mbostock self-assigned this Nov 6, 2015
@mbostock
Copy link
Member Author

mbostock commented Nov 6, 2015

Note that just removing the check @jasondavies added (|| 1 / value < 0) isn’t sufficient since the sign check needs to happen after rounding.

@kodi
Copy link

kodi commented Nov 6, 2015

Hi @mbostock, thanks for prompt response.

We are using 3.4.11, and not using format explicitly at all. (but we found that example online that i linked in tweet, and it turns out its not just in our scenario.

We are doing it like this:

setAxes: function() {
        var xyData = this.dataAsJson;
        var xExtent = d3.extent(xyData, function (d) {
            return d.x;
        });
        var yExtent = d3.extent(xyData, function (d) {
            return d.y;
        });
        if (xExtent[0] === xExtent[1]) {
            xExtent[1]++;
         }
        if (yExtent[0] === yExtent[1]) {
            yExtent[1]++;
        }
        this.x.domain(xExtent).nice();
        this.y.domain(yExtent).nice();
    }

if we remove .nice() the -0.00 disappears, but its probably because of new data range, and we are unable to test if we would get -0 without .nice()

Also, we really like the functionality that .nice() gives, and we would like to continue using it.

Is there any way that we could quick fix/override this behavior??

@mbostock
Copy link
Member Author

mbostock commented Nov 6, 2015

If you’re not using a number formatter (such d3.format or scale.tickFormat, or d3.svg.axis), you’re going to have precision issues regardless (e.g., a tick displaying as 0.30000000000000004 because in IEEE 754, that’s the result from 0.1 + 0.2). Tick values cannot be guaranteed to be nice round values because JavaScript does not support arbitrary precision arithmetic.

Yes, scale.nice affects the domain and the observed behavior, but it can either introduce or eliminate precision issues. The behavior depends on the exact domain in question and the number of ticks you’re requesting. As with scale.ticks, the scale.nice method cannot guarantee that the domain will have perfectly rounded values because of the limited precision of IEEE 754.

If you’re displaying numbers to humans, you should use a number format such as d3.format. If you’re testing whether a number is zero, you should test within epsilon, e.g., if (-1e-12 < x && x < 1e-12) rather than if (!x).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants