Skip to content

Commit 8b6d97c

Browse files
Enchufa2Copilot
andauthored
Fix sugar::unique for signed zeroes (#1404)
* Fix sugar::unique for signed zeroes * Fix typo Co-authored-by: Copilot <[email protected]> * Fix typo Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent 9f07c76 commit 8b6d97c

File tree

4 files changed

+43
-15
lines changed

4 files changed

+43
-15
lines changed

ChangeLog

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
2025-11-01 Iñaki Ucar <[email protected]>
2+
3+
* inst/include/Rcpp/hash/IndexHash.h: Normalize values for all comparisons
4+
* inst/include/Rcpp/hash/SelfHash.h: Idem
5+
* inst/tinytest/test_sugar.R: Add test for signed zeroes
6+
17
2025-10-21 Iñaki Ucar <[email protected]>
28

39
* inst/include/Rcpp/exceptions_impl.h: use __has_include to simplify checks

inst/include/Rcpp/hash/IndexHash.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
//
55
// Copyright (C) 2010, 2011 Simon Urbanek
66
// Copyright (C) 2012 - 2013 Dirk Eddelbuettel and Romain Francois
7-
// Copyright (C) 2014 - 2021 Dirk Eddelbuettel, Romain Francois and Kevin Ushey
7+
// Copyright (C) 2014 - 2024 Dirk Eddelbuettel, Romain Francois and Kevin Ushey
8+
// Copyright (C) 2025 Dirk Eddelbuettel, Romain Francois, Kevin Ushey and Iñaki Ucar
89
//
910
// This file is part of Rcpp.
1011
//
@@ -159,13 +160,15 @@ namespace Rcpp{
159160
#endif
160161
}
161162

163+
STORAGE normalize(STORAGE val) const { return val; }
164+
162165
inline bool not_equal(const STORAGE& lhs, const STORAGE& rhs) {
163-
return ! internal::NAEquals<STORAGE>()(lhs, rhs);
166+
return ! internal::NAEquals<STORAGE>()(normalize(lhs), rhs);
164167
}
165168

166169
bool add_value(int i){
167170
RCPP_DEBUG_2( "%s::add_value(%d)", DEMANGLE(IndexHash), i )
168-
STORAGE val = src[i++] ;
171+
STORAGE val = normalize(src[i++]);
169172
uint32_t addr = get_addr(val) ;
170173
while (data[addr] && not_equal( src[data[addr] - 1], val)) {
171174
addr++;
@@ -199,6 +202,15 @@ namespace Rcpp{
199202
uint32_t get_addr(STORAGE value) const ;
200203
} ;
201204

205+
template <>
206+
inline double IndexHash<REALSXP>::normalize(double val) const {
207+
/* double is a bit tricky - we have to normalize 0.0, NA and NaN */
208+
if (val == 0.0) val = 0.0;
209+
if (internal::Rcpp_IsNA(val)) val = NA_REAL;
210+
else if (internal::Rcpp_IsNaN(val)) val = R_NaN;
211+
return val;
212+
}
213+
202214
template <>
203215
inline uint32_t IndexHash<INTSXP>::get_addr(int value) const {
204216
return RCPP_HASH(value) ;
@@ -211,10 +223,6 @@ namespace Rcpp{
211223
uint32_t u[2];
212224
};
213225
union dint_u val_u;
214-
/* double is a bit tricky - we nave to normalize 0.0, NA and NaN */
215-
if (val == 0.0) val = 0.0;
216-
if (internal::Rcpp_IsNA(val)) val = NA_REAL;
217-
else if (internal::Rcpp_IsNaN(val)) val = R_NaN;
218226
val_u.d = val;
219227
addr = RCPP_HASH(val_u.u[0] + val_u.u[1]);
220228
return addr ;

inst/include/Rcpp/hash/SelfHash.h

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
// hash.h: Rcpp R/C++ interface class library -- hashing utility, inspired
44
// from Simon's fastmatch package
55
//
6-
// Copyright (C) 2010, 2011 Simon Urbanek
7-
// Copyright (C) 2012 Dirk Eddelbuettel and Romain Francois
6+
// Copyright (C) 2010, 2011 Simon Urbanek
7+
// Copyright (C) 2012 - 2024 Dirk Eddelbuettel and Romain Francois
8+
// Copyright (C) 2025 Dirk Eddelbuettel, Romain Francois and Iñaki Ucar
89
//
910
// This file is part of Rcpp.
1011
//
@@ -60,10 +61,16 @@ namespace sugar{
6061
std::vector<int> indices ;
6162
int size_ ;
6263

64+
STORAGE normalize(STORAGE val) const { return val; }
65+
66+
inline bool not_equal(const STORAGE& lhs, const STORAGE& rhs) {
67+
return ! internal::NAEquals<STORAGE>()(normalize(lhs), rhs);
68+
}
69+
6370
int add_value_get_index(int i){
64-
STORAGE val = src[i++] ;
71+
STORAGE val = normalize(src[i++]);
6572
unsigned int addr = get_addr(val) ;
66-
while (data[addr] && src[data[addr] - 1] != val) {
73+
while (data[addr] && not_equal( src[data[addr] - 1], val)) {
6774
addr++;
6875
if (addr == static_cast<unsigned int>(m)) addr = 0;
6976
}
@@ -90,6 +97,15 @@ namespace sugar{
9097
unsigned int get_addr(STORAGE value) const ;
9198
} ;
9299

100+
template <>
101+
inline double SelfHash<REALSXP>::normalize(double val) const {
102+
/* double is a bit tricky - we have to normalize 0.0, NA and NaN */
103+
if (val == 0.0) val = 0.0;
104+
if (internal::Rcpp_IsNA(val)) val = NA_REAL;
105+
else if (internal::Rcpp_IsNaN(val)) val = R_NaN;
106+
return val;
107+
}
108+
93109
template <>
94110
inline unsigned int SelfHash<INTSXP>::get_addr(int value) const {
95111
return RCPP_HASH(value) ;
@@ -102,10 +118,6 @@ namespace sugar{
102118
unsigned int u[2];
103119
};
104120
union dint_u val_u;
105-
/* double is a bit tricky - we nave to normalize 0.0, NA and NaN */
106-
if (val == 0.0) val = 0.0;
107-
if (internal::Rcpp_IsNA(val)) val = NA_REAL;
108-
else if (internal::Rcpp_IsNaN(val)) val = R_NaN;
109121
val_u.d = val;
110122
addr = RCPP_HASH(val_u.u[0] + val_u.u[1]);
111123
return addr ;

inst/tinytest/test_sugar.R

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,8 @@ expect_equal(sort(unique(x)), sort(runit_unique_dbl(x)), info = "unique / numeri
708708
x <- c(x, NA, NA)
709709
expect_equal(sort(unique(x), na.last = TRUE), sort(runit_unique_dbl(x), na.last = TRUE), info = "unique / numeric / with NA")
710710

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

712714
# test.sort_unique <- function() {
713715
set.seed(123)

0 commit comments

Comments
 (0)