更新: 根据其他人在源代码中发现的内容,我之前的说法是错误的 - sum()
不会排序。下面我发现的一致性模式源于排序(如下面某些情况所做的)和使用扩展精度中间值(如sum()
所做的)对精度具有相似的影响...
@user2357112 在下面进行了评论:
src/main/summary.c…没有进行任何排序。(对于求和操作来说,这将是一个很大的开销。)它甚至没有使用成对或补偿求和; 它只是以LDOUBLE(long double或double,取决于HAVE_LONG_DOUBLE)的方式从左到右单纯地累加所有数值。
我已经费尽心思在R源代码中寻找这个问题(未果 - sum
很难搜索),但我可以通过实验表明,在执行sum()
时,R将输入向量从最小到最大排序以最大化精度; 下面的sum()
和Reduce()
结果的差异是由于使用了扩展精度。 我不知道accu
是做什么的...
set.seed(101)
vec <- runif(100, 0, 0.00001)
options(digits=20)
(s1 <- sum(vec))
使用Reduce("+",...)
仅按顺序将元素相加。
(s2 <- Reduce("+",sort(vec)))
(s3 <- Reduce("+",vec))
identical(s1,s2)
?sum()
也提到:
在可能的情况下,会使用扩展精度累加器,但这取决于平台。
在已排序的向量上使用RcppArmadillo
进行操作,得到的答案与R中相同;而在原始顺序的向量上进行操作,则得到不同的答案(我不知道为什么;我的猜测是前面提到的扩展精度累加器,在数据未排序时会对数值结果产生更大的影响)。
suppressMessages(require(inline))
code <- '
arma::vec ax = Rcpp::as<arma::vec>(x);
return Rcpp::wrap(arma::accu(ax));
'
armasum <- cxxfunction(signature(x="numeric"),
code,plugin="RcppArmadillo")
(s4 <- armasum(vec))
(s5 <- armasum(sort(vec)))
identical(s1,s5)
但是评论中指出,这种方法并不适用于所有的seed:在这种情况下,Reduce()
的结果更接近于 sum()
的结果。
set.seed(123)
vec2 <- runif(50000,0,0.000001)
s4 <- sum(vec2); s5 <- Reduce("+",sort(vec2))
s6 <- Reduce("+",vec2); s7 <- armasum(sort(vec2))
rbind(s4,s5,s6,s7)
我被难住了。我本来期望至少s6
和s7
是相同的。
值得注意的是,通常情况下,当您的算法依赖于这些微小的数字差异时,您很可能会感到非常沮丧,因为结果很可能会因许多小的、可能超出您控制范围的因素(如特定操作系统、编译器等)而有所不同。
.Machine
文档很有用:http://stat.ethz.ch/R-manual/R-patched/library/base/html/zMachine.html - patrickmdnetrunif
之前使用set.seed
来使你的示例更具可重复性,这样其他人可以根据相同的测试用例进行检查。你还可以包含你的log
函数的代码吗? - nrussellsum
确实似乎将总和存储在此处定义的类型中这里。 - alexis_laz