根据 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