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

时间:2020-06-27 11:48:30

标签: r rcpp

根据https://stackoverflow.com/a/48676389/3846213https://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应该有一些特殊之处。

1 个答案:

答案 0 :(得分:4)

问题是乘法溢出。当你做

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
相关问题