Skip to content

Commit b4f4a54

Browse files
Out of range warning for as.integer64.character and hex support (#228)
* emit out of range exceptions for as.integer64.character * save expected warning as string, use strrep() to create giant number * cleaning up tests, check '0x0' * parameterized test with more warning cases * NEWS citations * missing ')' * tighten inclusions * parameterize tests for base-10 too, add a few more * avoid strlen() * clarify comment --------- Co-authored-by: Michael Chirico <chiricom@google.com>
1 parent cf47105 commit b4f4a54

4 files changed

Lines changed: 76 additions & 3 deletions

File tree

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
1. Coercion to/from integer64 is expanded greatly (includes #199). Thanks @hcirellu.
5454
- `as.Date`, `as.POSIXct`, `as.POSXlt`, `as.complex`, and `as.raw` get an `integer64` method.
5555
- `as.integer64` gets `Date`, `POSIXct`, `POSXlt`, `complex`, `raw`, and `difftime` methods.
56+
1. `as.integer64.character` now supports hexadecimal (base 16) input when prefixed with "0x" or "-0x", e.g. `as.integer64("0x7FFFFFFFFFFFFFFF")`. Thanks @hcirellu for a PR which completes work begun by @marcpaterno.
5657

5758
## BUG FIXES
5859

@@ -62,6 +63,7 @@
6263
1. `sortfin(integer64(), 1:10)` no longer segfaults (#164).
6364
1. `orderfin(as.integer64(10:1), 1:3, 8:11)` enforces that `table` be sorted by `order` instead of segfaulting (#166).
6465
1. `ordertab()` no longer segfaults when `nunique` is smaller than the actual number of unique values (#168).
66+
1. `as.integer64.character` now returns `NA` for out of range values, with warning, e.g. `as.integer64("22222222222222222222")`. Thanks @hcirellu.
6567

6668
## NOTES
6769

src/integer64.c

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <math.h> // floor
2121
#include <stdint.h>
2222
#include <stdlib.h> // strtoll
23+
#include <errno.h> // ERANGE
2324
#include <stdbool.h> // for boolean
2425

2526
#include <R.h>
@@ -154,13 +155,34 @@ SEXP as_integer64_character(SEXP x_, SEXP ret_){
154155
long long i, n = LENGTH(ret_);
155156
long long * ret = (long long *) REAL(ret_);
156157
const char * str;
158+
SEXP x_el;
157159
char * endpointer;
160+
Rboolean naflag = FALSE;
161+
int base;
158162
for(i=0; i<n; i++){
159-
str = CHAR(STRING_ELT(x_, i)); endpointer = (char *)str; // thanks to Murray Stokely 28.1.2012
160-
ret[i] = strtoll(str, &endpointer, 10);
161-
if (*endpointer)
163+
x_el = STRING_ELT(x_, i);
164+
if (x_el == NA_STRING){
162165
ret[i] = NA_INTEGER64;
166+
} else {
167+
base = 10; // default
168+
str = CHAR(x_el);
169+
if ((str[0]=='-' && str[1]=='0' && str[2]=='x') || (str[0]=='0' && str[1]=='x')) {
170+
base = 16;
171+
}
172+
endpointer = (char *)str; // thanks to Murray Stokely 28.1.2012
173+
errno = 0;
174+
ret[i] = strtoll(str, &endpointer, base);
175+
if (errno==ERANGE || *endpointer){
176+
ret[i] = NA_INTEGER64;
177+
naflag = TRUE;
178+
} else if (str==endpointer){
179+
ret[i] = NA_INTEGER64; // "" -> NA without warning
180+
} else if(ret[i]==NA_INTEGER64){ // i.e., received exact string value of NA_INTEGER64 sentinel
181+
naflag = TRUE;
182+
}
183+
}
163184
}
185+
if (naflag)warning(INTEGER64_NA_COERCION_WARNING);
164186
return ret_;
165187
}
166188

src/integer64.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#define INTEGER64_NAN_CREATED_WARNING "NaNs produced"
5656
#define INTEGER64_TODOUBLE_WARNING "integer precision lost while converting to double"
5757
#define BITSTRING_OVERFLOW_WARNING "bitstrings longer than 64 bytes converted to NA, multibyte-characters not allowed"
58+
#define INTEGER64_NA_COERCION_WARNING "NAs introduced by coercion to integer64 range"
5859

5960
#define PLUS64(e1,e2,ret,naflag) \
6061
if (e1 == NA_INTEGER64 || e2 == NA_INTEGER64) \

tests/testthat/test-integer64.R

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,54 @@ test_that("integer64 coercion to/from other types work for R >=4.0.0", {
139139

140140
})
141141

142+
test_that("integer64 coercion from character works for numbers near +/- 2^63", {
143+
expect_identical(
144+
as.character(as.integer64(c("-9223372036854775807", "-9223372036854775806", "9223372036854775806", "9223372036854775807", ""))),
145+
c("-9223372036854775807", "-9223372036854775806", "9223372036854775806", "9223372036854775807", NA)
146+
)
147+
})
148+
149+
test_that("Conversion from hex is supported", {
150+
expect_identical(
151+
as.integer64(c("0x1", "0xF", "0x7FFFFFFFFFFFFFFF", "-0x1", "-0xF", "-0x7FFFFFFFFFFFFFFF", "0x0")),
152+
as.integer64(c(1, 15, "9223372036854775807", -1, -15, "-9223372036854775807", 0))
153+
)
154+
})
155+
156+
with_parameters_test_that(
157+
"base-10 edge cases return missing",
158+
expect_warning(
159+
expect_identical(as.integer64(string), NA_integer64_),
160+
"NAs introduced by coercion to integer64 range", fixed=TRUE
161+
),
162+
string = c(
163+
strrep("9", 63L),
164+
"9223372036854775808",
165+
"-9223372036854775808",
166+
"999x",
167+
"-999x",
168+
"999 "
169+
)
170+
)
171+
172+
with_parameters_test_that(
173+
"hex edge cases return missing",
174+
expect_warning(
175+
expect_identical(as.integer64(string), NA_integer64_),
176+
"NAs introduced by coercion to integer64 range", fixed=TRUE
177+
),
178+
string = c(
179+
"-0x8000000000000000",
180+
"0x8000000000000000",
181+
"0x",
182+
"-0x",
183+
"0xx",
184+
" 0x",
185+
"0x ",
186+
"0x0Z"
187+
)
188+
)
189+
142190
test_that("S3 class basics work", {
143191
x = as.integer64(1:10)
144192
expect_s3_class(x, "integer64")

0 commit comments

Comments
 (0)