为什么在R中,指数运算(例如10^6)比计算器表示法(例如1e6)慢4倍?

20

在R代码中使用科学计数法10^6(我通常这样做)会导致比使用计算器表示法1e6更长的计算时间:

> system.time(for (t in 1:1e7) x=10^6) 
  utilisateur     système      écoulé 
        4.792       0.000       4.281 
> system.time(for (t in 1:1e7) x=1e6) 
 utilisateur     système      écoulé 
       0.804       0.000       1.051
> system.time(for (t in 1:1e7) x=exp(6*log(10)))
 utilisateur     système      écoulé 
       6.301       0.000       5.702

为什么R重新计算10^6的时间与计算exp{6*log(10)}的时间大致相同?我理解R在计算10^6时执行了一个函数,但为什么要这样编码呢?

2
我猜其中一个是数字字面量,只需要翻译它,而另一个是表达式,需要先进行求值。 - Ulrich Eckhardt
1
你认为为什么R会通过exp(6*log(10))计算出10^6 - cryo111
@cryo111: 我不知道R如何计算10^6,但它花费的时间与使用exp(6*log(10))一样长。我会重新表达这个句子,谢谢。 - Xi'an ні війні
2
这不是像你所说的“以特定方式编码”的问题,而是你明确告诉R计算10的6次方...例如,在C++中,你更愿意分配double a=1e6;还是double a=pow(10,6); - cryo111
1
@cryo111:我理解其中的原因,但使用幂符号时效率的损失让我感到遗憾。虽然这不是世界末日,但在我的未来代码中我必须小心谨慎处理这个问题。 - Xi'an ні війні
1
@西安 好问题,顺便说一句。此外,您得到了MrFlick和Josh两位的两个好答案,其中包含一些有趣的额外见解。+1 - cryo111
2个回答

37

这是因为1e6是一个常数,并且被解析器识别为常数,而10^6被解析为函数调用,需要进一步评估(通过调用函数^())。由于前者避免了昂贵的函数调用开销,因此评估它会快得多!

class(substitute(1e6))
# [1] "numeric"
class(substitute(10^6))
# [1] "call"

为了更清楚地看到这是一个电话,您可以像这样对其进行解析:

as.list(substitute(10^6))
# [[1]]
# `^`
# 
# [[2]]
# [1] 10
# 
# [[3]]
# [1] 6

还有几个有趣的案例:

## negative numbers are actually parsed as function calls
class(substitute(-1))
[1] "call"

## when you want an integer, 'L' notation lets you avoid a function call 
class(substitute(1000000L))
# [1] "integer"
class(substitute(as.integer(1000000)))
# [1] "call"

非常有趣的附加案例!但令人困惑。我以前从未听说过使用L(但在分配负数时似乎并不节省时间)。 - Xi'an ні війні
2
@西安 嗯,R语言中使用结尾的“L”来表示整数与其对“-”的处理是不同的。据我所知,没有任何方法可以提供负常量而不执行函数调用,比如没有“1N”类似的方式。 - Josh O'Brien

8
1e6的情况下,您正在指定一个字面数字值。那里没有任何计算发生。
当您请求10^6时,R会将10提高到6次幂的工作。 ^是一个数字运算符。它对10的基数没有特殊要求。它不知道10^612^14之间的区别。它必须进行计算才能找到答案。

谢谢。虽然很遗憾10没有被赋予特殊地位! - Xi'an ні війні
3
给10个特殊状态会使解析器变得更加复杂。尽管我没有进行测试,但很可能会比偶尔的幂运算计算更加拖慢速度。 - kasterma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接