Skip to content

Commit ebea1a2

Browse files
author
maechler
committed
using deprecated options(OutDec = "<not-1-char>") now warns in print() etc
git-svn-id: https://svn.r-project.org/R/trunk@87474 00db46b3-68df-0310-9c12-caf00c1e9a41
1 parent 8b97965 commit ebea1a2

File tree

6 files changed

+71
-27
lines changed

6 files changed

+71
-27
lines changed

doc/NEWS.Rd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,10 @@
424424
an error in the future, when \code{s} is not a string with exactly
425425
one character.
426426
427+
\item When \code{s <- getOption("OutDec")} is not a string of one
428+
character, a warning is signalled now whenever it is used in internal
429+
C code, notably when calling the default methods of \code{format()}.
430+
427431
\item \code{pwilcox()} and \code{qwilcox()} now check for user
428432
interrupt less frequently.
429433
}

src/main/paste.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ attribute_hidden SEXP do_format(SEXP call, SEXP op, SEXP args, SEXP env)
515515
my_OutDec = OutDec; // default
516516
else {
517517
static char sdec[11];
518-
#define _WARN_decimal_mark_non_1 // were *not* warning here by default since 2015-06-19
518+
#undef _WARN_decimal_mark_non_1 /* as we now warn in EncodeReal0() */
519519
#ifdef _WARN_decimal_mark_non_1
520520
if(R_nchar(STRING_ELT(CAR(args), 0), Chars,
521521
/* allowNA = */ FALSE, /* keepNA = */ FALSE,

src/main/printarray.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
Used only for row/column names found by GetMatrixDimnames,
4242
so in native encoding. (NULL ones from do_prmatrix are skipped.)
4343
*/
44-
int Rstrwid(const char *str, int slen, int enc, int quote); /* from printutils.c */
44+
int Rstrwid(const char *str, int slen, cetype_t ienc, int quote); /* from printutils.c */
4545
#define strwidth(x) Rstrwid(x, (int) strlen(x), CE_NATIVE, 0)
4646

4747
/* ceil_DIV(a,b) := ceil(a / b) in _int_ arithmetic : */

src/main/printutils.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ const char *EncodeExtptr(SEXP x)
180180
return buf;
181181
}
182182

183+
int Rstrwid(const char *str, int slen, cetype_t ienc, int quote); /* below */
184+
#define strwidth(x) Rstrwid(x, (int) strlen(x), CE_NATIVE, 0)
185+
183186
attribute_hidden
184187
const char *EncodeReal(double x, int w, int d, int e, char cdec)
185188
{
@@ -202,22 +205,26 @@ const char *EncodeReal0(double x, int w, int d, int e, const char *dec)
202205
else snprintf(buff, NB, "%*s", min(w, (NB-1)), "-Inf");
203206
}
204207
else if (e) {
205-
if(d) {
208+
if(d) { // '#' flag
206209
snprintf(fmt, 20, "%%#%d.%de", min(w, (NB-1)), d);
207-
snprintf(buff, NB, fmt, x);
208210
}
209211
else {
210212
snprintf(fmt, 20, "%%%d.%de", min(w, (NB-1)), d);
211-
snprintf(buff, NB, fmt, x);
212213
}
214+
snprintf(buff, NB, fmt, x);
213215
}
214216
else { /* e = 0 */
215217
snprintf(fmt, 20, "%%%d.%df", min(w, (NB-1)), d);
216218
snprintf(buff, NB, fmt, x);
217219
}
218220
buff[NB-1] = '\0';
219221

220-
if(strcmp(dec, ".")) {
222+
if(strcmp(dec, ".")) { /* replace "." by dec */
223+
int len = strwidth(dec); /* 3·14 must work */
224+
if(len != 1) warning(
225+
_("the decimal mark is %s than one character wide; this will become an error"),
226+
(len > 1) ? "more" : "less");
227+
221228
char *p, *q;
222229
for(p = buff, q = buff2; *p; p++) {
223230
if(*p == '.') for(const char *r = dec; *r; r++) *q++ = *r;
@@ -230,6 +237,7 @@ const char *EncodeReal0(double x, int w, int d, int e, const char *dec)
230237
return out;
231238
}
232239

240+
// A copy of EncodeReal0() -- additionally dropping trailing zeros:
233241
static const char
234242
*EncodeRealDrop0(double x, int w, int d, int e, const char *dec)
235243
{
@@ -247,12 +255,11 @@ static const char
247255
else if (e) {
248256
if(d) {
249257
snprintf(fmt, 20, "%%#%d.%de", min(w, (NB-1)), d);
250-
snprintf(buff, NB, fmt, x);
251258
}
252259
else {
253260
snprintf(fmt, 20, "%%%d.%de", min(w, (NB-1)), d);
254-
snprintf(buff, NB, fmt, x);
255261
}
262+
snprintf(buff, NB, fmt, x);
256263
}
257264
else { /* e = 0 */
258265
snprintf(fmt, 20, "%%%d.%df", min(w, (NB-1)), d);
@@ -274,7 +281,12 @@ static const char
274281
}
275282
}
276283

277-
if(strcmp(dec, ".")) {
284+
if(strcmp(dec, ".")) { /* replace "." by dec */
285+
int len = strwidth(dec); /* 3·14 must work */
286+
if(len != 1) warning(
287+
_("the decimal mark is %s than one character wide; this will become an error"),
288+
(len > 1) ? "more" : "less");
289+
278290
char *p, *q;
279291
for(p = buff, q = buff2; *p; p++) {
280292
if(*p == '.') for(const char *r = dec; *r; r++) *q++ = *r;
@@ -1063,7 +1075,7 @@ void REvprintf(const char *format, va_list arg)
10631075

10641076
int attribute_hidden IndexWidth(R_xlen_t n)
10651077
{
1066-
return (int) (log10(n + 0.5) + 1);
1078+
return (int) (log10((double)n + 0.5) + 1);
10671079
}
10681080

10691081
attribute_hidden void VectorIndex(R_xlen_t i, int w)

tests/reg-encodings.R

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,3 +372,14 @@ r16[7] <- as.raw(0x00) # invalid (unpaired surrogate)
372372
r16[8] <- as.raw(0xd8)
373373
stopifnot(identical(iconv(list(r16), "UTF-16", "UTF-8", sub="byte"),
374374
"He<00><d8>lo world"))
375+
376+
377+
## Using a __unicode__ decimal mark is fine :
378+
op <- options(OutDec = "·", scipen = 1)
379+
x <- pi* 10^(-6:5)
380+
fx <- sapply(x, format)
381+
print(fx, width=88, quote=FALSE) # 3·141593e-06 0·00003141593 0·0003141593 ....
382+
options(OutDec = ".") # back to normal
383+
stopifnot(grepl("·", fx, fixed=TRUE),
384+
identical(sub("·", ".", fx), sapply(x, format)))
385+
options(op)

tests/reg-tests-1e.R

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
.pt <- proc.time()
44
tryCid <- function(expr) tryCatch(expr, error = identity)
55
tryCmsg<- function(expr) tryCatch(expr, error = conditionMessage) # typically == *$message
6-
assertErrV <- function(...) tools::assertError(..., verbose=TRUE)
6+
assertErrV <- function(...) tools::assertError (..., verbose=TRUE)
7+
assertWarnV <- function(...) tools::assertWarning(..., verbose=TRUE)
78
`%||%` <- function (L, R) if(is.null(L)) R else L
89
##' get value of `expr` and keep warning as attribute (if there is one)
910
getVaW <- function(expr) {
@@ -464,8 +465,8 @@ mod2 <- local({
464465
offset = { print("world"); x-y })
465466
}) # rank-deficient in "subtle" way {warning/NA may not be needed}; just show for now:
466467
nd <- data.frame(x = 1:5)
467-
tools::assertWarning(print(predict(mod2, newdata=nd, rankdeficient = "warnif")))
468-
predict(mod2, newdata=nd, rankdeficient = "NA")
468+
assertWarnV(print(predict(mod2, newdata=nd, rankdeficient = "warnif")))
469+
predict(mod2, newdata=nd, rankdeficient = "NA") # NA's but no warning
469470
nm5 <- as.character(1:5)
470471
stopifnot(exprs = {
471472
all.equal(setNames(rep(0, 5), nm5), predict(mod2), tol=1e-13) # pred: 1.776e-15
@@ -507,7 +508,7 @@ stopifnot(all.equal(urf$root, 0.88653, tolerance = 1e-4))
507508

508509

509510
## chkDots() in subset.data.frame() to prevent usage errors
510-
tools::assertWarning(subset(data.frame(y = 1), y = 2), verbose = TRUE)
511+
assertWarnV(subset(data.frame(y = 1), y = 2))
511512
## R < 4.3.0 was silent about unused ... arguments
512513

513514

@@ -518,8 +519,8 @@ a <- 1:2
518519
assertErrV(a:1) # numerical expression has length > 1
519520
assertErrV(2:a) # " " " "
520521
Sys.unsetenv("_R_CHECK_LENGTH_COLON_")
521-
tools::assertWarning(s1 <- a:1, verbose=TRUE)
522-
tools::assertWarning(s2 <- 2:a, verbose=TRUE)
522+
assertWarnV(s1 <- a:1)
523+
assertWarnV(s2 <- 2:a)
523524
stopifnot(identical(s1, 1L), identical(s2, 2:1))
524525
Sys.setenv("_R_CHECK_LENGTH_COLON_" = oldV)# reset
525526
## always only warned in R <= 4.2.z
@@ -802,11 +803,9 @@ km1d <- kappa(m, norm = "1", method = "direct")
802803
all.equal(km1d, 7.6, tol=0) # 1.17e-16 {was wrongly 11.907 in R <= 4.3.1}
803804
## 2) kappa(z, norm="2", LINPACK=TRUE) silently returns estimate of the *1*-norm cond.nr.
804805
(km1 <- kappa(m, norm = "1")) # 4.651847 {unchanged}
805-
tools::assertWarning(verbose=TRUE, # now *warns*
806-
km2L <- kappa(m, norm="2", LINPACK=TRUE))
806+
assertWarnV( km2L <- kappa(m, norm="2", LINPACK=TRUE)) # now *warns*
807807
## 3) kappa(z, norm="2", LINPACK=FALSE) throws an error
808-
tools::assertWarning(verbose=TRUE, # *same* warning (1-norm instead of 2-)
809-
km2La <- kappa(m, norm="2", LINPACK=FALSE))
808+
assertWarnV(km2La <- kappa(m, norm="2", LINPACK=FALSE))# same warning (1-norm instead of 2-)
810809
km2La
811810
## 4) kappa.qr(z) implicitly assumes nrow(z$qr) >= ncol(z$qr), not true in general
812811
(kqrm2 <- kappa(qr(cbind(m, m + 1))))
@@ -853,10 +852,8 @@ stopifnot(exprs = {
853852
(zm <- m + 1i*c(1,-(1:2))*(m/4))
854853
(kz1d <- kappa(zm, norm = "1", method = "direct"))
855854
(kz1 <- kappa(zm, norm = "1"))# meth = "qr"
856-
tools::assertWarning(verbose=TRUE, # now *warns* {gave *error* previously}
857-
kz2L <- kappa(zm, norm="2", LINPACK=TRUE))
858-
tools::assertWarning(verbose=TRUE, # *same* warning (1-norm instead of 2-)
859-
kz2La <- kappa(zm, norm="2", LINPACK=FALSE))
855+
assertWarnV(kz2L <- kappa(zm, norm="2", LINPACK=TRUE))# now *warns* {gave *error* previously}
856+
assertWarnV(kz2La <- kappa(zm, norm="2", LINPACK=FALSE))# same warning (1-norm instead of 2-)
860857
kz2La
861858
## 4) kappa.qr(z) implicitly assumes nrow(z$qr) >= ncol(z$qr) ..
862859
(kzqr2 <- kappa(qr(cbind(zm, zm + 1)))) # gave Error .. matrix should be square
@@ -1026,7 +1023,7 @@ stopifnot(identical(tt, drop.terms(tt, dropx = 0[0], keep.response=TRUE)))
10261023

10271024

10281025
## as.complex("<num>i") -- should work (and fail/warn) as the parser does:
1029-
tools::assertWarning(cc <- as.complex("12iL"), verbose=TRUE)
1026+
assertWarnV( cc <- as.complex("12iL"))
10301027
tools::assertWarning(cF <- as.complex("12irene"))
10311028
tools::assertWarning(cI <- as.complex("12I"))
10321029
stopifnot(is.na(cc), is.na(cF), is.na(cI),
@@ -1477,6 +1474,8 @@ if(attr(oL, "ok") && capabilities("NLS") && !is.na(.popath)
14771474
stopifnot(is.character(print("checking 'out' : ")),
14781475
grepl("^argument non num.rique pour un ", out))
14791476
## was *not* switched to French (when this was run via 'make ..')
1477+
## reset {just in case}:
1478+
Sys.setLanguage("en")
14801479

14811480

14821481
## print( ls.str() ) using '<missing>' also in non-English setup:
@@ -1605,9 +1604,9 @@ assertErrV(options(scipen = NULL))# would work (but ..) in R <= 4.4.2
16051604
assertErrV(options(scipen = 1:2)) # would just work
16061605
assertErrV(options(scipen = 1e99))# would "work" w/ 2 warnings and invalid setting
16071606
stopifnot(identical(getOption("scipen"), scipenO))# unchanged
1608-
tools::assertWarning(verbose=TRUE, options(scipen = -100 ))# warns and sets to min = -9
1607+
assertWarnV(options(scipen = -100))# warns and sets to min = -9
16091608
stopifnot(identical(getOption("scipen"), -9L))
1610-
tools::assertWarning(verbose=TRUE, options(scipen = 100000))# warns and sets to max = 9999
1609+
assertWarnV(options(scipen = 100000))# warns and sets to max = 9999
16111610
stopifnot(identical(getOption("scipen"), 9999L))
16121611
## setting to NULL would invalidate as.character(Sys.time())
16131612

@@ -1678,6 +1677,24 @@ sort.int(x, method = "quick")
16781677
sort.int(x, method = "quick", index.return = TRUE)
16791678

16801679

1680+
## More warning for _illegal_ OutDec -- even auto print() ing now warns when OutDec is illegal:
1681+
assertWarnV(op <- options(OutDec = "_._", scipen = 6, warn = 1))
1682+
assertWarnV( print(pi) ) # _new_ warning ... "will become an error"
1683+
writeLines(m <- capture.output(format(pi), type = "message"))
1684+
## Warning in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, :
1685+
## the decimal mark is more than one character wide; this will become an error
1686+
assertWarnV(options(OutDec = ""))
1687+
m2 <- tryCatch(print(pi), warning = conditionMessage)
1688+
assertWarnV( print(pi * 10^(-4:4)) ) # _new_ warning
1689+
if(englishMsgs) stopifnot(exprs = {
1690+
grepl("^Warning in prettyNum\\(\\.Internal\\(format\\(", m[1])
1691+
grepl("the decimal mark is more than one character wide", m[length(m)])
1692+
grepl("the decimal mark is less than one character wide", m2)
1693+
})
1694+
## now warn from format() and (only once) from print()
1695+
options(op) # return to sanity + warn=2
1696+
1697+
16811698

16821699
## keep at end
16831700
rbind(last = proc.time() - .pt,

0 commit comments

Comments
 (0)