Skip to content
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
6 changes: 6 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
2025-11-01 Iñaki Ucar <iucar@fedoraproject.org>

* inst/include/Rcpp/hash/IndexHash.h: Normalize values for all comparisons
* inst/include/Rcpp/hash/SelfHash.h: Idem
* inst/tinytest/test_sugar.R: Add test for signed zeroes

2025-10-21 Iñaki Ucar <iucar@fedoraproject.org>

* inst/include/Rcpp/exceptions_impl.h: use __has_include to simplify checks
Expand Down
22 changes: 15 additions & 7 deletions inst/include/Rcpp/hash/IndexHash.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
//
// Copyright (C) 2010, 2011 Simon Urbanek
// Copyright (C) 2012 - 2013 Dirk Eddelbuettel and Romain Francois
// Copyright (C) 2014 - 2021 Dirk Eddelbuettel, Romain Francois and Kevin Ushey
// Copyright (C) 2014 - 2024 Dirk Eddelbuettel, Romain Francois and Kevin Ushey
// Copyright (C) 2025 Dirk Eddelbuettel, Romain Francois, Kevin Ushey and Iñaki Ucar
//
// This file is part of Rcpp.
//
Expand Down Expand Up @@ -159,13 +160,15 @@ namespace Rcpp{
#endif
}

STORAGE normalize(STORAGE val) const { return val; }

inline bool not_equal(const STORAGE& lhs, const STORAGE& rhs) {
return ! internal::NAEquals<STORAGE>()(lhs, rhs);
return ! internal::NAEquals<STORAGE>()(normalize(lhs), rhs);
}

bool add_value(int i){
RCPP_DEBUG_2( "%s::add_value(%d)", DEMANGLE(IndexHash), i )
STORAGE val = src[i++] ;
STORAGE val = normalize(src[i++]);
uint32_t addr = get_addr(val) ;
while (data[addr] && not_equal( src[data[addr] - 1], val)) {
addr++;
Expand Down Expand Up @@ -199,6 +202,15 @@ namespace Rcpp{
uint32_t get_addr(STORAGE value) const ;
} ;

template <>
inline double IndexHash<REALSXP>::normalize(double val) const {
/* double is a bit tricky - we have to normalize 0.0, NA and NaN */
if (val == 0.0) val = 0.0;
if (internal::Rcpp_IsNA(val)) val = NA_REAL;
else if (internal::Rcpp_IsNaN(val)) val = R_NaN;
return val;
}

template <>
inline uint32_t IndexHash<INTSXP>::get_addr(int value) const {
return RCPP_HASH(value) ;
Expand All @@ -211,10 +223,6 @@ namespace Rcpp{
uint32_t u[2];
};
union dint_u val_u;
/* double is a bit tricky - we nave to normalize 0.0, NA and NaN */
if (val == 0.0) val = 0.0;
if (internal::Rcpp_IsNA(val)) val = NA_REAL;
else if (internal::Rcpp_IsNaN(val)) val = R_NaN;
val_u.d = val;
addr = RCPP_HASH(val_u.u[0] + val_u.u[1]);
return addr ;
Expand Down
28 changes: 20 additions & 8 deletions inst/include/Rcpp/hash/SelfHash.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
// hash.h: Rcpp R/C++ interface class library -- hashing utility, inspired
// from Simon's fastmatch package
//
// Copyright (C) 2010, 2011 Simon Urbanek
// Copyright (C) 2012 Dirk Eddelbuettel and Romain Francois
// Copyright (C) 2010, 2011 Simon Urbanek
// Copyright (C) 2012 - 2024 Dirk Eddelbuettel and Romain Francois
// Copyright (C) 2025 Dirk Eddelbuettel, Romain Francois and Iñaki Ucar
//
// This file is part of Rcpp.
//
Expand Down Expand Up @@ -60,10 +61,16 @@ namespace sugar{
std::vector<int> indices ;
int size_ ;

STORAGE normalize(STORAGE val) const { return val; }

inline bool not_equal(const STORAGE& lhs, const STORAGE& rhs) {
return ! internal::NAEquals<STORAGE>()(normalize(lhs), rhs);
}

int add_value_get_index(int i){
STORAGE val = src[i++] ;
STORAGE val = normalize(src[i++]);
unsigned int addr = get_addr(val) ;
while (data[addr] && src[data[addr] - 1] != val) {
while (data[addr] && not_equal( src[data[addr] - 1], val)) {
addr++;
if (addr == static_cast<unsigned int>(m)) addr = 0;
}
Expand All @@ -90,6 +97,15 @@ namespace sugar{
unsigned int get_addr(STORAGE value) const ;
} ;

template <>
inline double SelfHash<REALSXP>::normalize(double val) const {
/* double is a bit tricky - we have to normalize 0.0, NA and NaN */
if (val == 0.0) val = 0.0;
if (internal::Rcpp_IsNA(val)) val = NA_REAL;
else if (internal::Rcpp_IsNaN(val)) val = R_NaN;
return val;
}

template <>
inline unsigned int SelfHash<INTSXP>::get_addr(int value) const {
return RCPP_HASH(value) ;
Expand All @@ -102,10 +118,6 @@ namespace sugar{
unsigned int u[2];
};
union dint_u val_u;
/* double is a bit tricky - we nave to normalize 0.0, NA and NaN */
if (val == 0.0) val = 0.0;
if (internal::Rcpp_IsNA(val)) val = NA_REAL;
else if (internal::Rcpp_IsNaN(val)) val = R_NaN;
val_u.d = val;
addr = RCPP_HASH(val_u.u[0] + val_u.u[1]);
return addr ;
Expand Down
2 changes: 2 additions & 0 deletions inst/tinytest/test_sugar.R
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,8 @@ expect_equal(sort(unique(x)), sort(runit_unique_dbl(x)), info = "unique / numeri
x <- c(x, NA, NA)
expect_equal(sort(unique(x), na.last = TRUE), sort(runit_unique_dbl(x), na.last = TRUE), info = "unique / numeric / with NA")

x <- c(x, -0.0, +0.0)
expect_equal(sort(unique(x)), sort(runit_unique_dbl(x)), info = "unique / numeric / with signed 0s")

# test.sort_unique <- function() {
set.seed(123)
Expand Down
Loading