Skip to content

Commit 06e9678

Browse files
mns-nordicalst-kalinowski
authored andcommitted
Emit Re() with an explicit c_double kind
Re() lowered to kind-less real(arg). For a complex argument that is correct (F2018: the result takes the argument's kind), but the handler accepts any mode, and for a double or integer argument kind-less real() is single precision — so Re(0.1) returned 0.100000001490116, silently rounded through a float while declared real(c_double). Emit real(arg, kind=c_double), which is exact for double/integer arguments and a no-op change of meaning for complex ones.
1 parent 69505bb commit 06e9678

3 files changed

Lines changed: 82 additions & 2 deletions

File tree

R/r2f-math.R

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ r2f_handlers[["abs"]] <- function(args, scope, ...) {
131131
register_unary_intrinsic(
132132
"Re",
133133
mode_fun = function(arg) "double",
134-
expr_fun = function(arg, intrinsic) glue("real({arg})")
134+
# kind-less real() of an integer or double argument is single precision;
135+
# kind=c_double is a no-op change of meaning for complex arguments (F2018).
136+
expr_fun = function(arg, intrinsic) glue("real({arg}, kind=c_double)")
135137
)
136138

137139
register_unary_intrinsic(

tests/testthat/_snaps/unary-intrinsics.md

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1691,7 +1691,7 @@
16911691
! manifest end
16921692
16931693
1694-
out = real(z)
1694+
out = real(z, kind=c_double)
16951695
end subroutine
16961696
Code
16971697
cat(cwrapper)
@@ -1982,6 +1982,69 @@
19821982
return out;
19831983
}
19841984

1985+
# Re() on a double argument keeps double precision
1986+
1987+
Code
1988+
fn
1989+
Output
1990+
function(x) {
1991+
declare(type(x = double(n)))
1992+
out <- Re(x)
1993+
out
1994+
}
1995+
<environment: 0x0>
1996+
Code
1997+
cat(fsub)
1998+
Output
1999+
subroutine fn(x, out, x__len_) bind(c)
2000+
use iso_c_binding, only: c_double, c_ptrdiff_t
2001+
implicit none
2002+
2003+
! manifest start
2004+
! sizes
2005+
integer(c_ptrdiff_t), intent(in), value :: x__len_
2006+
2007+
! args
2008+
real(c_double), intent(in) :: x(x__len_)
2009+
real(c_double), intent(out) :: out(x__len_)
2010+
! manifest end
2011+
2012+
2013+
out = real(x, kind=c_double)
2014+
end subroutine
2015+
Code
2016+
cat(cwrapper)
2017+
Output
2018+
#define R_NO_REMAP
2019+
#include <R.h>
2020+
#include <Rinternals.h>
2021+
2022+
2023+
extern void fn(
2024+
const double* const x__,
2025+
double* const out__,
2026+
const R_xlen_t x__len_);
2027+
2028+
SEXP fn_(SEXP _args) {
2029+
// x
2030+
_args = CDR(_args);
2031+
SEXP x = CAR(_args);
2032+
if (TYPEOF(x) != REALSXP) {
2033+
Rf_error("typeof(x) must be 'double', not '%s'", Rf_type2char(TYPEOF(x)));
2034+
}
2035+
const double* const x__ = REAL(x);
2036+
const R_xlen_t x__len_ = Rf_xlength(x);
2037+
2038+
const R_xlen_t out__len_ = x__len_;
2039+
SEXP out = PROTECT(Rf_allocVector(REALSXP, out__len_));
2040+
double* out__ = REAL(out);
2041+
2042+
fn(x__, out__, x__len_);
2043+
2044+
UNPROTECT(1);
2045+
return out;
2046+
}
2047+
19852048
# logical not local used as ifelse mask compiles and runs
19862049

19872050
Code

tests/testthat/test-unary-intrinsics.R

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,21 @@ test_that("complex unary intrinsics", {
106106
})
107107

108108

109+
test_that("Re() on a double argument keeps double precision", {
110+
fn <- function(x) {
111+
declare(type(x = double(n)))
112+
out <- Re(x)
113+
out
114+
}
115+
116+
# locks real(x, kind=c_double): kind-less real() of a double argument
117+
# is single precision, so Re() silently rounded its input
118+
expect_translation_snapshots(fn)
119+
120+
expect_quick_identical(fn, list(c(0.1, 1 / 3, -2.5)))
121+
})
122+
123+
109124
test_that("unary logical 'not'-operator on vector", {
110125
fn <- function(x) {
111126
declare(type(x = integer(n)))

0 commit comments

Comments
 (0)