Issue with small axis limits

I have difficulties setting exact axis limits at very small values.

For example, the following code:

A = 1e-1;
x=-1:0.1:1;
plot(x,A*x.^3)
ylim([-1 1]*A);

works as expected, i.e., creates a plot with y-axis limits exactly [-A A], only when A > 1e-8. For smaller values of A the resulting limits are approx. [-1.2*A 1.2*A].

My system

  • OS: Ubuntu 20.04
  • Octave version: 7.1.0

Hy gapost, welcome to Octave discourse group :slight_smile:

You can chose your limits with an if statement as:

A = 1e-1;
h = .1;

x = -1:h:1;
plot(x,A*x.^3);

if (A > 1e-8)
  ylim([-1.2 1.2]*A);
else
  ylim([-1 1]*A);
endif

Or in other case you can make a function that consider limits from an input that you give

function lim = limits(A)
  lim = A*(1.2 - (A - 1e-8)*.2)*[-1 1];
endfunction

A = 1e-1;
h = .1;

x = -1:h:1;
plot(x,A*x.^3);

ylim(limits(A));

I hope that solves the problem, regards!.

1 Like

Hi @89453728, thank you for your reply.

What you propose may help in a particular case, however, in general one should be able to set the desired limits simply by calling ylim.

There is this chunk of code in axes::properties::check_axis_limits:
octave: libinterp/corefcn/graphics.cc annotate (gnu.org)

  // FIXME: maybe this test should also be relative?
  else if (! logscale && (std::abs (limits(0) - limits(1)) < sqrt (eps)))
    {
      limits(0) -= 0.1 * std::abs (limits(0));
      limits(1) += 0.1 * std::abs (limits(1));
      do_update = true;
    }

sqrt (eps) is sqrt (eps ("double")) (approx. 1.5e-8). That is most probably the cause why the axis limits are starting to get “weird” at this point.
The FIXME note was likely referring to something like your example…

I don’t recall the motivation for this code snippet. Possibly, I wanted to avoid same values for the upper and lower limits for, e.g., a single horizontal line in the axes. But the current test seems kind of arbitrary to me now…
At the same time, I’m not sure what would be a better test…

Edit: Maybe I thought about the condition that is used for log-scale axes when I wrote “should also be relative”?
std::abs (std::log10 (limits(0) / limits(1))) < sqrt (eps)

@mmuetzel thank you for the reply.

I think the code in check_axis_limits tries to avoid a situation where the limits become indistinguishable due to computer round-off error.

However, eps refers to relative errors while the code makes an absolute comparison.

Maybe something like the code below is needed?

double dl = std::abs ( limits(0) - limits(1) );
double m = std::max( std::abs(limits(0)), std::abs(limits(1)) ); 
if ( dl / m < sqrt (eps) )
{
    // fix the limits
}

I sent a bug report about this issue: GNU Octave - Bugs: bug #62481, Cannot set exactly xlim / ylim at... [Savannah]

1 Like

That report is about setting limits at even lower values. At some point, we’d probably need to treat the limits as “basically the same” and automatically adjust them to get to something that can actually be reasonably rendered. But the current condition for that might be questionable…

We could either repurpose that report for that issue or open a new one.

Not sure if this condition is enough. IIUC, it would allow arbitrarily small distance between lower and upper limits as long as they are centered around zero. That won’t work at too small values because of the limited precision of single precision floating point numbers (even taking subnormals into account).

On the other hand, your condition does “approximately” the right thing when it comes to offsets of the data IIUC.