Rcpp R 向量大小限制(不允许负长度向量)

  • 本文关键字:向量 不允许 Rcpp r rcpp
  • 更新时间 :
  • 英文 :


根据 https://stackoverflow.com/a/48676389/3846213 和 https://stackoverflow.com/a/5234293/3846213 R向量限制为2^31 - 1项。但是,我已经能够通过 Rcpp 在该数字的一半处触发"不允许负长度向量"错误(这应该是尝试分配过大向量的标志(。这一切都来自我尝试调试基于 Rcpp 的 R 包 (https://github.com/tpq/propr/issues/13(。

library(Rcpp)
cppFunction("
IntegerVector test(int size) {
int veclen = size * (size - 1) / 2;
IntegerVector vec(veclen);
return vec;
}
")
vec <- test(47000)
Error in test(47000) : negative length vectors are not allowed

47000^2/2 几乎是 2^31 的一半。 我在纯 R 中没有这样的问题,也就是说vec <- 1:(47000*(47000-1)/2)运行良好,所以 Rcpp 应该有一些特别之处。

问题是乘法溢出。当你这样做时

size * (size - 1) / 2

操作顺序咬你,因为

size * (size - 1)

即使整体表达式没有溢出,也可能溢出。 我们可以通过添加打印语句来看到这一点:

IntegerVector test(int size) {
int veclen = size * (size - 1) / 2;
Rcpp::Rcout << veclen << std::endl;
IntegerVector vec(veclen);
return vec;
}
vec <- test(47000)
# -1043007148

因此,我们可以通过更改该操作的方式来修复它:

IntegerVector test(int size) {
int veclen = (size / 2) * (size - 1);
Rcpp::Rcout << veclen << std::endl;
IntegerVector vec(veclen);
return vec;
}

这没有问题

vec <- test(47000)
# 1104476500
str(vec)
# int [1:1104476500] 0 0 0 0 0 0 0 0 0 0 ...

更新:奇数问题

Eli Korvigo在关于奇数的整数除法行为的评论中提出了一个很好的观点。为了说明,请考虑调用偶数 4 和奇数 5 的函数

even <- 4
odd  <- 5
even * (even - 1) / 2
# [1] 6
odd  * (odd  - 1) / 2
# [1] 10

它应该分别创建长度为 6 和 10 的向量。 但是,会发生什么?

test(4)
# 6
# [1] 0 0 0 0 0 0
test(5)
# 8
# [1] 0 0 0 0 0 0 0 0

哦不! 整数除法中的5 / 2是 2,而不是 2.5,因此这在奇数情况下并不完全符合我们的需求。 但是,幸运的是,我们可以通过简单的流量控制轻松解决此问题:

IntegerVector test2(int size) {
int veclen;
if ( size % 2 == 0 ) {
veclen = (size / 2) * (size - 1);
} else {
veclen = size * ((size - 1) / 2);
}
Rcpp::Rcout << veclen << std::endl;
IntegerVector vec(veclen);
return vec;
}

我们可以看到这很好地处理了奇数和偶数情况:

test2(4)
# 6
# [1] 0 0 0 0 0 0
test2(5)
# 10
# [1] 0 0 0 0 0 0 0 0 0 0

最新更新