20

Using the scientific notation 10^6 in an R code (as I customarily do) results in a significantly longer computing time than using the calculator representation 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

Why is it the case that R recomputes 10^6 in about the same times as it computes exp{6*log(10)}? I understand the fact that R executes a function when computing 10^6, but why was it coded this way?

  • 2
    I'd guess that one is a numeric literal that only needs to be translated while the other is an expression that first needs to be evaluated. – Ulrich Eckhardt May 02 '15 at 15:59
  • 1
    Why do you think that R calculates `10^6` via `exp(6*log(10))`? – cryo111 May 02 '15 at 16:13
  • @cryo111: I do not know how R computes `10^6`but it takes as long as using `exp(6*log(10))`. I will rephrase this sentence, thank you. – Xi'an ні війні May 02 '15 at 16:18
  • 2
    It's not a matter of "being coded in a particular way" as you say but that you are explicitly telling R to compute 10 to the power of 6... For instance, would you rather assign `double a=1e6;` or `double a=pow(10,6);` in C++? – cryo111 May 02 '15 at 16:19
  • 1
    @cryo111: I understand the reason but bemoan the loss in efficiency when using a power notation. This is not the end of the world, but I have to be careful about this in my future codes. – Xi'an ні війні May 02 '15 at 16:23
  • 1
    @Xi'an A good question BTW. Also, you got two nice answers with some interesting additional insights by MrFlick and Josh. +1 all – cryo111 May 02 '15 at 16:29

2 Answers2

37

It's because 1e6 is a constant, and is recognized as such by the parser, whereas 10^6 is parsed as a function call which has to be further evaluated (by a call to the function ^()). Since the former avoids the expensive overhead of a function call, evaluating it is a lot faster!

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

To better see that it's a call, you can dissect it like this:

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

A few other interesting cases:

## 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"
Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
  • Very interesting additional cases! But puzzling. I had never heard of using the L before (but it does not seem to save time when allocating a negative number) – Xi'an ні війні May 02 '15 at 16:16
  • 2
    @Xi'an Yeah, R's use of a trailing `L` to indicate integers is distinct from its treatment of `-` as a function call. As far as I know, there's simply no way to supply a negative constant without executing a function call --- no `"1N"` or the like. – Josh O'Brien May 02 '15 at 16:23
8

In the case of 1e6 you are specifying a literal numeric value. There is no calculation happening there.

When you request 10^6, R does the work of raising 10 to the 6th power. The ^ is a numeric operator. It doesn't do anything special for base 10. It doesn't know the different between 10^6 and 12^14. It must do the calculation to find the answer.

MrFlick
  • 195,160
  • 17
  • 277
  • 295