Rcpp R 向量大小限制(不允许负长度向量)
Posted
技术标签:
【中文标题】Rcpp R 向量大小限制(不允许负长度向量)【英文标题】:Rcpp R vector size limit (negative length vectors are not allowed) 【发布时间】:2020-06-27 11:48:30 【问题描述】:根据https://***.com/a/48676389/3846213 和https://***.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应该有一些特别的东西。
【问题讨论】:
@Jan 是的,你是对的。有溢出。我想,我现在可以删除我的问题了。 @Jan 您愿意发表一个答案,以便我接受。你帮了我很多忙。 不,@duckmayr 是对的。我可以复制你的问题。但这不仅仅是溢出。我们必须继续寻找。 @Jan 实际上是溢出,但只是由于操作顺序;请看下面我的回答 【参考方案1】:问题是乘法溢出。当你这样做时
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 在 cmets 中提出了一个关于奇数整数除法行为的精彩观点。为了说明,考虑用偶数 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
【讨论】:
感谢您重申我对该算术表达式的疑虑。 不幸的是,重新排序会给您带来另一种麻烦:整数除法。veclen = size * (size - 1) / 2;
对奇数和偶数的计算相同。但是,(size / 2) * (size - 1)
对于奇数和偶数的行为不同。
@EliKorvigo 好点!很抱歉我最初忽略了这一点。我已经在答案的更新中解决了这个问题。以上是关于Rcpp R 向量大小限制(不允许负长度向量)的主要内容,如果未能解决你的问题,请参考以下文章