Skip to content

Commit

Permalink
more consistent behavior of complex number NaN / NA - not back-compat…
Browse files Browse the repository at this point in the history
…ible

git-svn-id: https://svn.r-project.org/R/trunk@69410 00db46b3-68df-0310-9c12-caf00c1e9a41
  • Loading branch information
maechler committed Sep 22, 2015
1 parent 4fff4c5 commit 4a4c205
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 18 deletions.
8 changes: 6 additions & 2 deletions doc/NEWS.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@
\code{match(x, table)} is faster, sometimes by an order of magnitude,
when \code{x} is of length one, % and \code{incomparables} is unchanged
thanks to Haverty's \PR{16491}.
More consistent, partly not back-compatible behavior of \code{NA}
and \code{NaN} coercion to complex numbers, operations less often
resulting in complex \code{NA} (\code{NA_complex_}).
}
}
Expand Down Expand Up @@ -370,10 +374,10 @@
\item \code{abbreviate()} did not give names to the return value
if \code{minlength} was zero, unlike when it was positive.
\item (Windows only) \code{dir.create()} did not always warn
when it failed to create a directory. (\PR{16537})
\item When operating in a multibyte locale, \code{grep()}
and related functions did not handle UTF-8 strings properly.
(\PR{16264})
Expand Down
48 changes: 39 additions & 9 deletions src/library/base/man/complex.Rd
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
% File src/library/base/man/complex.Rd
% Part of the R package, https://www.R-project.org
% Copyright 1995-2010 R Core Team
% Copyright 1995-2015 R Core Team
% Distributed under GPL 2 or later

\name{complex}
\title{Complex Vectors}
\title{Complex Numbers and Basic Functionality}
\alias{complex}
\alias{as.complex}
\alias{is.complex}
Expand All @@ -14,7 +14,8 @@
\alias{Arg}
\alias{Conj}
\description{
Basic functions which support complex arithmetic in R.
Basic functions which support complex arithmetic in \R, in addition to
the arithmetic operators \code{+}, \code{-}, \code{*}, \code{/}, and \code{^}.
}
\usage{
complex(length.out = 0, real = numeric(), imaginary = numeric(),
Expand Down Expand Up @@ -48,8 +49,15 @@ Conj(z)

\code{as.complex} attempts to coerce its argument to be of complex
type: like \code{\link{as.vector}} it strips attributes including
names. All forms of \code{NA} and \code{NaN} are coerced to a complex
\code{NA}, for which both the real and imaginary parts are \code{NA}.
names. Up to \R versions 3.2.x, all forms of \code{NA} and \code{NaN}
were coerced to a complex \code{NA}, i.e., the \code{\link{NA_complex_}}
constant, for which both the real and imaginary parts are \code{NA}.
Since \R 3.3.0, typically only objects which are \code{NA} in parts
are coerced to complex \code{NA}, but others with \code{NaN} parts,
are \emph{not}. As a consequence, complex arithmetic where only
\code{NaN}'s (but no \code{NA}'s) are involved typically will
\emph{not} give complex \code{NA} but complex numbers with real or
imaginary parts of \code{NaN}.

Note that \code{is.complex} and \code{is.numeric} are never both
\code{TRUE}.
Expand All @@ -67,14 +75,36 @@ Conj(z)
individually or \emph{via} the \code{\link[=S3groupGeneric]{Complex}}
group generic.

In addition, the elementary trigonometric, logarithmic, exponential,
square root and hyperbolic functions are implemented for complex
values.
In addition to the arithmetic operators (see \link{Arithmetic})
\code{+}, \code{-}, \code{*}, \code{/}, and \code{^}, the elementary
trigonometric, logarithmic, exponential, square root and hyperbolic
functions are implemented for complex values.

Matrix multiplications (\code{\link{\%*\%}}, \code{\link{crossprod}},
\code{\link{tcrossprod}}) are also defined for complex matrices
(\code{\link{matrix}}), and so are \code{\link{solve}},
\code{\link{eigen}} or \code{\link{svd}}.

Internally, complex numbers are stored as a pair of \link{double}
precision numbers, either or both of which can be \code{\link{NaN}} or
precision numbers, either or both of which can be \code{\link{NaN}}
(including \code{NA}, see \code{\link{NA_complex_}} and above) or
plus or minus infinity.
}
\note{
Operations and functions involving complex \code{\link{NaN}} mostly
rely on the C library's handling of \samp{double complex} arithmetic,
which typically returns \code{complex(re=NaN, im=NaN)} (but we have
not seen a guarantee for that).
For \code{+} and \code{-}, \R's own handling works strictly
\dQuote{coordinate wise}.

Operations involving complex \code{NA}, i.e., \code{\link{NA_complex_}}, return
\code{\link{NA_complex_}}.
}
\seealso{
\code{\link{Arithmetic}}; \code{\link{polyroot}} finds all \eqn{n}
complex roots of a polynomial of degree \eqn{n}.
}
\section{S4 methods}{
\code{as.complex} is primitive and can have S4 methods set.

Expand Down
22 changes: 16 additions & 6 deletions src/main/coerce.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ RealFromComplex(Rcomplex x, int *warn)
{
if (ISNAN(x.r) || ISNAN(x.i))
return NA_REAL;
if (ISNAN(x.r)) return x.r;
if (ISNAN(x.i)) return NA_REAL;
if (x.i != 0)
*warn |= WARN_IMAG;
return x.r;
Expand Down Expand Up @@ -255,14 +257,18 @@ Rcomplex attribute_hidden
ComplexFromReal(double x, int *warn)
{
Rcomplex z;
#ifdef PRE_R_3_3_0
if (ISNAN(x)) {
z.r = NA_REAL;
z.i = NA_REAL;
}
else {
#endif
z.r = x;
z.i = 0;
#ifdef PRE_R_3_3_0
}
#endif
return z;
}

Expand Down Expand Up @@ -316,7 +322,8 @@ SEXP attribute_hidden StringFromComplex(Rcomplex x, int *warn)
{
int wr, dr, er, wi, di, ei;
formatComplex(&x, 1, &wr, &dr, &er, &wi, &di, &ei, 0);
if (ISNA(x.r) || ISNA(x.i)) return NA_STRING;
if (ISNA(x.r) || ISNA(x.i)) // "NA" if Re or Im is (but not if they're just NaN)
return NA_STRING;
else /* EncodeComplex has its own anti-trailing-0 care :*/
return mkChar(EncodeComplex(x, wr, dr, er, wi, di, ei, OutDec));
}
Expand Down Expand Up @@ -2024,8 +2031,8 @@ SEXP attribute_hidden do_isna(SEXP call, SEXP op, SEXP args, SEXP rho)
LOGICAL(ans)[i] = (STRING_ELT(s, 0) == NA_STRING); \
break; \
case CPLXSXP: \
LOGICAL(ans)[i] = (ISNAN(COMPLEX(s)[0].r) \
|| ISNAN(COMPLEX(s)[0].i)); \
LOGICAL(ans)[i] = (ISNAN(COMPLEX(s)[0].r) || \
ISNAN(COMPLEX(s)[0].i)); \
break; \
default: \
LOGICAL(ans)[i] = 0; \
Expand Down Expand Up @@ -2247,7 +2254,8 @@ SEXP attribute_hidden do_isnan(SEXP call, SEXP op, SEXP args, SEXP rho)
R_IsNaN(COMPLEX(x)[i].i));
break;
default:
errorcall(call, _("default method not implemented for type '%s'"), type2char(TYPEOF(x)));
errorcall(call, _("default method not implemented for type '%s'"),
type2char(TYPEOF(x)));
}
if (dims != R_NilValue)
setAttrib(ans, R_DimSymbol, dims);
Expand Down Expand Up @@ -2310,7 +2318,8 @@ SEXP attribute_hidden do_isfinite(SEXP call, SEXP op, SEXP args, SEXP rho)
LOGICAL(ans)[i] = (R_FINITE(COMPLEX(x)[i].r) && R_FINITE(COMPLEX(x)[i].i));
break;
default:
errorcall(call, _("default method not implemented for type '%s'"), type2char(TYPEOF(x)));
errorcall(call, _("default method not implemented for type '%s'"),
type2char(TYPEOF(x)));
}
if (dims != R_NilValue)
setAttrib(ans, R_DimSymbol, dims);
Expand Down Expand Up @@ -2381,7 +2390,8 @@ SEXP attribute_hidden do_isinfinite(SEXP call, SEXP op, SEXP args, SEXP rho)
}
break;
default:
errorcall(call, _("default method not implemented for type '%s'"), type2char(TYPEOF(x)));
errorcall(call, _("default method not implemented for type '%s'"),
type2char(TYPEOF(x)));
}
if (!isNull(dims))
setAttrib(ans, R_DimSymbol, dims);
Expand Down
26 changes: 26 additions & 0 deletions tests/complex.R
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,29 @@ atan(z)
## but they seem to assume signed zeros.
## Windows gave incorrect (NaN) values on the cuts.

## Not a regression test, but rather one of the good cases:
(cNaN <- as.complex("NaN"))
stopifnot(identical(cNaN, complex(re = NaN)), is.nan(Re(cNaN)), Im(cNaN) == 0)
dput(cNaN) ## (real = NaN, imaginary = 0)
## Partly new behavior:
(c0NaN <- complex(real=0, im=NaN))
(cNaNaN <- complex(re=NaN, im=NaN))
stopifnot(identical(cNaN, as.complex(NaN)),
identical(vapply(c(cNaN, c0NaN, cNaNaN), format, ""),
c("NaN+0i", "0+NaNi", "NaN+NaNi")),
identical(cNaN, NaN + 0i),
identical(cNaN, Conj(cNaN)),
identical(cNaN, cNaN+cNaN),

identical(cNaNaN, 1i * NaN),
identical(cNaNaN, complex(modulus= NaN)),
identical(cNaNaN, complex(argument= NaN)),
identical(cNaNaN, complex(arg=NaN, mod=NaN)),

identical(c0NaN, c0NaN+c0NaN), # !
identical(NA_complex_, NaN + NA_complex_ ),
## Probably TRUE, but by a standard ??
## identical(cNaNaN, 2 * c0NaN), # C-library arithmetic
## identical(cNaNaN, 2 * cNaN), # C-library arithmetic
## identical(cNaNaN, NA_complex_ * Inf),
TRUE)
32 changes: 31 additions & 1 deletion tests/complex.Rout.save
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

R Under development (unstable) (2015-09-22 r69408) -- "Unsuffered Consequences"
R Under development (unstable) (2015-09-22 r69407) -- "Unsuffered Consequences"
Copyright (C) 2015 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)

Expand Down Expand Up @@ -260,4 +260,34 @@ n= 30 : 465+0i -15+142.7155i -15+70.56945i -15+46.16525i -15+33.69055i -15+25.98
> ## but they seem to assume signed zeros.
> ## Windows gave incorrect (NaN) values on the cuts.
>
> ## Not a regression test, but rather one of the good cases:
> (cNaN <- as.complex("NaN"))
[1] NaN+0i
> stopifnot(identical(cNaN, complex(re = NaN)), is.nan(Re(cNaN)), Im(cNaN) == 0)
> dput(cNaN) ## (real = NaN, imaginary = 0)
complex(real=NaN, imaginary=0)
> ## Partly new behavior:
> (c0NaN <- complex(real=0, im=NaN))
[1] 0+NaNi
> (cNaNaN <- complex(re=NaN, im=NaN))
[1] NaN+NaNi
> stopifnot(identical(cNaN, as.complex(NaN)),
+ identical(vapply(c(cNaN, c0NaN, cNaNaN), format, ""),
+ c("NaN+0i", "0+NaNi", "NaN+NaNi")),
+ identical(cNaN, NaN + 0i),
+ identical(cNaN, Conj(cNaN)),
+ identical(cNaN, cNaN+cNaN),
+
+ identical(cNaNaN, 1i * NaN),
+ identical(cNaNaN, complex(modulus= NaN)),
+ identical(cNaNaN, complex(argument= NaN)),
+ identical(cNaNaN, complex(arg=NaN, mod=NaN)),
+
+ identical(c0NaN, c0NaN+c0NaN), # !
+ identical(NA_complex_, NaN + NA_complex_ ),
+ ## Probably TRUE, but by a standard ??
+ ## identical(cNaNaN, 2 * c0NaN), # C-library arithmetic
+ ## identical(cNaNaN, 2 * cNaN), # C-library arithmetic
+ ## identical(cNaNaN, NA_complex_ * Inf),
+ TRUE)
>

0 comments on commit 4a4c205

Please sign in to comment.