Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New function (as.double2), plus documentation and tests. #1

Merged
merged 1 commit into from
Feb 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ LinkingTo: Rcpp
Suggests: tinytest
URL: https://github.com/eddelbuettel/rcppfastfloat/
BugReports: https://github.com/eddelbuettel/rcppfastfloat/issues
RoxygenNote: 6.0.1
RoxygenNote: 7.1.1
Roxygen: list(markdown = TRUE)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't change it now, but I have a slight preference for the old markdown 6.0.1 which I have in an opt-in directory. It does not do the stooopid dance of forcing a recompilation and rebuild only to deal with Rd creation. And with that I often say no to markdown too. (At least in stull-small projects such as this. Exceptions can be made and are made.)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also realize that it is awkward with collaborators who won't have 6.0.1 around. Ah well. RStudio lacks flexibility in invoking alternate converters, sadly.

Copy link
Collaborator Author

@knapply knapply Feb 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, just help remind me if I forget/don't get to it before you're looking to ship to CRAN.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably keep it. I am on the fence on md-in-Rd too. It's on in some projects, I am just not too consisten. No worries right now. This all looks svelte as usual.

Encoding: UTF-8
35 changes: 35 additions & 0 deletions R/RcppExports.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,41 @@
# Generated by using Rcpp::compileAttributes() -> do not edit by hand
# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393

#' Ultra efficient string-to-`double` Conversion
#'
#' For `character` `vector`s,`as.double2()` is a drop-in replacement for `base::as.double()`.
#'
#' @param x A vector of type `character`.
#'
#' @seealso [base::as.double()]
#'
#' @examples
#' set.seed(8675309)
#' input <- sample(c(
#' paste0(" \r\n\t\f\v", c(0.0, sqrt(seq(1, 10))), " \r\n\t\f\v"),
#' c("NaN", "-NaN", "nan", "-nan",
#' "Inf", "-Inf", "inf", "-inf", "infinity", "-infinity",
#' NA_character_,
#' " 1970-01-01", "1970-01-02 ")
#' ))
#' input
#'
#' suppressWarnings(as.double2(input)) # NAs introduced by coercion
#'
#' comparison <- suppressWarnings(
#' matrix(c(as.double(input), as.double2(input)),
#' ncol = 2L,
#' dimnames = list(NULL, c("as.double()", "as.double2()")))
#' )
#' comparison
#'
#' all.equal(comparison[, "as.double()"], comparison[, "as.double2()"])
#'
#' @export
as.double2 <- function(x) {
.Call(`_RcppFastFloat_as_dbl`, x)
}

#' Floating Point Parsing Example
#'
#' This example is adapted from the example of the upstream README.md file, and generalized
Expand Down
13 changes: 13 additions & 0 deletions inst/tinytest/test_asdouble2.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
library(RcppFastFloat)

set.seed(8675309)
input <- sample(c(
paste0(" \r\n\t\f\v", c(0.0, sqrt(seq(1, 10))), " \r\n\t\f\v"),
c("NaN", "-NaN", "nan", "-nan",
"Inf", "-Inf", "inf", "-inf", "infinity", "-infinity",
NA_character_,
" 1970-01-01", "1970-01-02 ")
))

expect_equal(suppressWarnings(as.double(input)),
suppressWarnings(as.double2(input)))
40 changes: 40 additions & 0 deletions man/as.double2.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions src/RcppExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@

using namespace Rcpp;

#ifdef RCPP_USE_GLOBAL_ROSTREAM
Rcpp::Rostream<true>& Rcpp::Rcout = Rcpp::Rcpp_cout_get();
Rcpp::Rostream<false>& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get();
#endif

// as_dbl
Rcpp::DoubleVector as_dbl(const Rcpp::CharacterVector x);
RcppExport SEXP _RcppFastFloat_as_dbl(SEXP xSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< const Rcpp::CharacterVector >::type x(xSEXP);
rcpp_result_gen = Rcpp::wrap(as_dbl(x));
return rcpp_result_gen;
END_RCPP
}
// parseExample
double parseExample(const std::string& input, bool verbose);
RcppExport SEXP _RcppFastFloat_parseExample(SEXP inputSEXP, SEXP verboseSEXP) {
Expand All @@ -19,6 +35,7 @@ END_RCPP
}

static const R_CallMethodDef CallEntries[] = {
{"_RcppFastFloat_as_dbl", (DL_FUNC) &_RcppFastFloat_as_dbl, 1},
{"_RcppFastFloat_parseExample", (DL_FUNC) &_RcppFastFloat_parseExample, 2},
{NULL, NULL, 0}
};
Expand Down
73 changes: 73 additions & 0 deletions src/as_double2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#define STRICT_R_HEADERS
#include <Rcpp.h>

#include "fast_float/fast_float.h"

/* essentially isBlankString(), but returns bool instead of Rboolean */
bool is_only_whitespace(const char *s) noexcept {
while (*s) {
if (!std::isspace(static_cast<unsigned char>(*s))) {
return false;
}
++s;
}
return true;
}

//' Ultra efficient string-to-`double` Conversion
//'
//' For `character` `vector`s,`as.double2()` is a drop-in replacement for `base::as.double()`.
//'
//' @param x A vector of type `character`.
//'
//' @seealso [base::as.double()]
//'
//' @examples
//' set.seed(8675309)
//' input <- sample(c(
//' paste0(" \r\n\t\f\v", c(0.0, sqrt(seq(1, 10))), " \r\n\t\f\v"),
//' c("NaN", "-NaN", "nan", "-nan",
//' "Inf", "-Inf", "inf", "-inf", "infinity", "-infinity",
//' NA_character_,
//' " 1970-01-01", "1970-01-02 ")
//' ))
//' input
//'
//' suppressWarnings(as.double2(input)) # NAs introduced by coercion
//'
//' comparison <- suppressWarnings(
//' matrix(c(as.double(input), as.double2(input)),
//' ncol = 2L,
//' dimnames = list(NULL, c("as.double()", "as.double2()")))
//' )
//' comparison
//'
//' all.equal(comparison[, "as.double()"], comparison[, "as.double2()"])
//'
//' @export
// [[Rcpp::export(as.double2)]]
Rcpp::DoubleVector
as_dbl(const Rcpp::CharacterVector x) {
const auto n{x.size()};
Rcpp::DoubleVector out(n, NA_REAL);
bool any_failures{false};

for (R_xlen_t i = 0; i < n; ++i) {
const char *cstring = CHAR(x[i]);
double d;
const auto res = fast_float::from_chars(
cstring, cstring + std::char_traits<char>::length(cstring), d);

if (res.ec == std::errc() && is_only_whitespace(res.ptr)) {
out[i] = d;
} else {
any_failures = true;
}
}

if (any_failures) {
Rcpp::warning("NAs introduced by coercion");
}

return out;
}