Skip to content

Commit 7182176

Browse files
author
maechler
committed
pretty(): fix boundary cases for eps.correct=2 (+ enhance docu)
git-svn-id: https://svn.r-project.org/R/trunk@88880 00db46b3-68df-0310-9c12-caf00c1e9a41
1 parent 0096213 commit 7182176

File tree

4 files changed

+54
-21
lines changed

4 files changed

+54
-21
lines changed

doc/NEWS.Rd

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
\subsection{NEW FEATURES}{
1818
\itemize{
19-
\item The default \code{AIC} and \code{BIC} methods now check whether
19+
\item The default \code{AIC} and \code{BIC} methods now check whether
2020
the \pkg{methods} namespace is loaded rather than \pkg{stats4}. They
2121
still use \code{stats4::logLik} (and \code{stats4::nobs}), but packages
2222
no longer need to load \pkg{stats4} explicitly.
@@ -394,6 +394,9 @@
394394

395395
\item The Cairo-based SVG device uses \code{pt} as the default
396396
document unit also with Cairo >= 1.17.8 (\PR{18912}).
397+
398+
\item \code{pretty(*, eps.correct = 2)} has been fixed, e.g., to
399+
avoid over 1 mio length result for \code{pretty(c(0, 1e-322), eps.correct = 2)}.
397400
}
398401
}
399402
}

src/appl/pretty.c

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
attribute_hidden
7676
double R_pretty(double *lo, double *up, int *ndiv, int min_n,
7777
double shrink_sml,
78-
const double high_u_fact[], // = (h, h5, f_min) below
78+
const double high_u_fact[], // = (h, h5, f_min) below
7979
int eps_correction, int return_bounds)
8080
{
8181
/* From version 0.65 on, we had rounding_eps := 1e-5, before, r..eps = 0
@@ -110,7 +110,7 @@ double R_pretty(double *lo, double *up, int *ndiv, int min_n,
110110
#ifdef DEBUGpr
111111
REprintf("R_pretty(lo=%g,up=%g,ndiv=%d,min_n=%d,shrink=%g,high_u=(%g,%g,%g),eps=%d,bnds=%d)"
112112
"\n\t => dx=%g; i_small:%s. ==> first cell=%g\n",
113-
lo_, up_, *ndiv, min_n, shrink_sml, h, h5, min_f,
113+
lo_, up_, *ndiv, min_n, shrink_sml, h, h5, f_min,
114114
eps_correction, return_bounds,
115115
dx, i_small ? "TRUE" : "F", cell);
116116
#endif
@@ -142,11 +142,12 @@ double R_pretty(double *lo, double *up, int *ndiv, int min_n,
142142
if(subsmall == 0.) // subnormals underflowing to zero (not yet seen!)
143143
subsmall = DBL_MIN;
144144
if(cell < subsmall) { // possibly subnormal
145-
warning(_("R_pretty(): very small range 'cell'=%g, corrected to %g"),
145+
if(cell > 0)
146+
warning(_("R_pretty(): very small range 'cell'=%.3g, increased to %g"),
146147
cell, subsmall);
147148
cell = subsmall;
148149
} else if(cell > DBL_MAX/MAX_F) {
149-
warning(_("R_pretty(): very large range 'cell'=%g, corrected to %g"),
150+
warning(_("R_pretty(): very large range 'cell'=%.3g, decreased to %g"),
150151
cell, DBL_MAX/MAX_F);
151152
cell = DBL_MAX/MAX_F;
152153
}
@@ -181,9 +182,18 @@ double R_pretty(double *lo, double *up, int *ndiv, int min_n,
181182
cell, base, unit, ns, nu);
182183
#endif
183184
if(eps_correction && (eps_correction > 1 || !i_small)) {
184-
// FIXME?: assumes 0 <= lo <= up (what if lo <= up < 0 ?)
185-
if(lo_ != 0.) *lo *= (1- DBL_EPSILON); else *lo = -DBL_MIN;
186-
if(up_ != 0.) *up *= (1+ DBL_EPSILON); else *up = +DBL_MIN;
185+
#define E_ DBL_EPSILON
186+
const double D_max = DBL_MAX*(1. - ldexp(E_, -1));
187+
/* move *lo to the left, assuming <subnorm>*(1-E_) does not underflow to 0 : */
188+
if(lo_ < 0.) *lo *= (1+E_); else if(lo_ > 0) *lo *= (1-E_); else *lo = -fmin2(unit, DBL_MIN);
189+
/* and *up to the right : */
190+
if(up_ < 0.) *up *= (1-E_); else if(up_ > 0.) {
191+
if(up_ < D_max) *up *= (1+E_);} else *up = +fmin2(unit, DBL_MIN);
192+
#undef E_
193+
#ifdef DEBUGpr
194+
REprintf(" eps_correction (assuming lo=%g <= %g=up): new *lo=%g, *up=%g\n",
195+
lo_, up_, *lo, *up);
196+
#endif
187197
}
188198

189199
#ifdef DEBUGpr
@@ -194,7 +204,7 @@ double R_pretty(double *lo, double *up, int *ndiv, int min_n,
194204

195205
#ifdef DEBUGpr
196206
if(!R_FINITE(ns*unit))
197-
REprintf("\t infinite (ns=%.0f)*(unit=%g) ==> ns++\n", ns, unit);
207+
REprintf("\t while(!finite((ns=%.0f)*(unit=%g))) ns++\n", ns, unit);
198208
#endif
199209
while(!R_FINITE(ns*unit)) ns++;
200210

@@ -207,7 +217,7 @@ double R_pretty(double *lo, double *up, int *ndiv, int min_n,
207217

208218
#ifdef DEBUGpr
209219
if(!R_FINITE(nu*unit))
210-
REprintf("\t infinite (nu=%.0f)*(unit=%g) ==> nu--\n", nu, unit);
220+
REprintf("\t while(!finite((nu=%.0f)*(unit=%g)) nu--\n", nu, unit);
211221
#endif
212222
while(!R_FINITE(nu*unit)) nu--;
213223

@@ -246,7 +256,7 @@ double R_pretty(double *lo, double *up, int *ndiv, int min_n,
246256
if(ns * unit < *lo) *lo = ns * unit;
247257
if(nu * unit > *up) *up = nu * unit;
248258
#endif
249-
} else { // used in graphics GEpretty(), hence grid::grid.pretty()
259+
} else { // used in graphics GEPretty(), hence grid::grid.pretty()
250260
*lo = ns;
251261
*up = nu;
252262
}

src/library/base/man/pretty.Rd

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
% File src/library/base/man/pretty.Rd
22
% Part of the R package, https://www.R-project.org
3-
% Copyright 1995-2021 R Core Team
3+
% Copyright 1995-2025 R Core Team
44
% Distributed under GPL 2 or later
55

66
\name{pretty}
@@ -35,23 +35,26 @@ pretty(x, \dots)
3535
\item{u5.bias}{non-negative numeric
3636
multiplier favoring factor 5 over 2. Default and \sQuote{optimal}:
3737
\code{u5.bias = .5 + 1.5*high.u.bias}.}
38-
\item{eps.correct}{integer code, one of \{0,1,2\}. If non-0, an
38+
\item{eps.correct}{integer code, one of \{0,1,2\}. If non-0, an
3939
\emph{epsilon correction} is made at the boundaries such that
4040
the result boundaries will be outside \code{range(x)}; in the
41-
\emph{small} case, the correction is only done if \code{eps.correct >= 2}.}
41+
\emph{small} case, this correction happens only if \code{eps.correct >= 2},
42+
allowing \code{eps.correct = 2} to reproduce how \R's graphics engine
43+
computes axis tick locations, see \sQuote{Details}.}
4244
\item{f.min}{positive factor multiplied by \code{\link{.Machine}$double.xmin}
4345
to get the smallest \dQuote{acceptable} \code{cell} \eqn{c_m} which
4446
determines the \code{unit} of the algorithm. Smaller \code{cell}
4547
values are set to \eqn{c_n} signalling a \code{\link{warning}} about
4648
being \dQuote{corrected}.
4749
New from \R 4.2.0,: previously \code{f.min = 20} was
4850
hardcoded in the algorithm.}
49-
\item{bounds}{a \code{\link{logical}} indicating if the resulting vector
51+
\item{bounds}{a \code{\link{logical}} indicating if the resulting vector
5052
should \emph{cover} the full \code{range(x)}, i.e., strictly include
51-
the bounds of \code{x}. New from \R 4.2.0, allowing \code{bound=FALSE}
52-
to reproduce how \R's graphics engine computes axis tick locations (in
53-
\code{GEPretty()}).}
54-
\item{\dots}{further arguments for methods.}
53+
the bounds of \code{x}. New from \R 4.2.0, allowing \code{bounds=FALSE}
54+
to reproduce how \R's graphics engine computes axis extents and tick
55+
locations, see \sQuote{Details}; also, for \code{.pretty()}, it determines
56+
which parts of the C level computations should be returned.}
57+
\item{\dots}{further arguments for methods; unused in default method.}
5558
}
5659
\description{
5760
Compute a sequence of about \code{n+1} equally spaced \sQuote{round}
@@ -75,13 +78,22 @@ pretty(x, \dots)
7578
=}\code{high.u.bias} and \eqn{f =}\code{u5.bias}.
7679

7780
\dots\dots\dots
81+
82+
83+
\R's graphics engine \code{GEPretty()} C function calls % ../../../main/engine.c
84+
\code{R_pretty()} and is used in both packages \pkg{graphics} and
85+
\pkg{grid} (and hence \CRANpkg{lattice}, \CRANpkg{ggplot2}, etc.) for
86+
axis range and tick computations. For these, partly for back
87+
compatibility reasons, the optional arguments are set, corresponding to
88+
\preformatted{
89+
(min.n = 1, shrink.sml = 0.25, high.u.bias = 0.8, u5.bias = 1.7,
90+
f.min = 1.125, eps.correct = 2, bounds = FALSE)}
7891
}
7992
\value{\code{pretty()} returns an numeric vector of \emph{approximately}
8093
\code{n} increasing numbers which are \dQuote{pretty} in decimal notation.
8194
(in extreme range cases, the numbers can no longer be \dQuote{pretty}
8295
given the other constraints; e.g., for \code{pretty(..)} % <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< FIXME
83-
84-
For ease of investigating the underlying C \code{R_pretty()}
96+
For ease of investigating the underlying C \code{R_pretty()} % ../../../appl/pretty.c
8597
function, \code{.pretty()} returns a named \code{\link{list}}. By
8698
default, when \code{bounds=TRUE}, the entries are \code{l}, \code{u},
8799
and \code{n}, whereas for \code{bounds=FALSE}, they are

tests/reg-tests-1e.R

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2286,6 +2286,14 @@ stopifnot(exprs = {
22862286
}) ## were all FALSE in R <= 4.5.1: `last = 1000000L' was not large enough
22872287

22882288

2289+
## pretty(<very small>, eps.correct=2) would produce huge vectors
2290+
assertWarnV(pp <- .pretty(c(0, 1e-322), eps.correct = 2))
2291+
str(pp)
2292+
E <- 2e-314
2293+
stopifnot(all.equal(list(l = -E, u = E, n = 2L), pp, tolerance = 1e-12))
2294+
## n = 1112538 (Lnx 64b) in R <= 4.5.1 ^^^^^^
2295+
2296+
22892297

22902298
## keep at end
22912299
rbind(last = proc.time() - .pt,

0 commit comments

Comments
 (0)