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

Rcpp R vector size limit (negative length vectors are not allowed)

根据 and R vectors are limited 2^31 - 1 items. However, I've been able to trigger the "negative length vectors are not allowed" error (which is supposed to be a sign of trying to allocate an overly large vector) at half that number via Rcpp. This all comes from my trying to debug an Rcpp-based R package (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