# C++11 constexpr: computing exp at compile time

While trying out constexpr I was wondering what else can I get compiler to compute. I can pretty much do any integral computation with both C++ Template Metaprogramming and constexpr, but can I do some floating point computations? It appears I can!!

So I set out to compute exp(z), using Taylor series implementation for exp:

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25  // can't deal with negative powers!! // constexpr double pow(double x, int y) { return y == 0 ? 1.0 : x * pow(x, y-1); } constexpr int factorial(int x) { return x == 0 ? 1 : x * factorial(x-1); } constexpr double exp(double x) { return 1.0 + x + pow(x,2)/factorial(2) + pow(x, 3)/factorial(3) + pow(x, 4)/factorial(4) + pow(x, 5)/factorial(5) + pow(x,6)/factorial(6) + pow(x, 7)/factorial(7) + pow(x, 8)/factorial(8) + pow(x, 9)/factorial(9); } int main() { constexpr double exp1 = exp(1.0); return 0; } 

In here I have pow function to compute powers of a floating point number, mine is bit limited as it can do integral powers only; factorial straight forward. exp function function goes to 10 component evaluation. With exp(1) I do see value of 2.7182815255731922 in debug window while cout prints it to be 2.71828. Since this is floating point value I can’t do static_assert.

Now what’s the proof that it was all compile time rather than runtime evaluation, the only way to confirm is to look at what is executed by processor, that’s right assembly:

cpp-cxprmain at main.cpp:388:
0x100000cc0:  pushq  %rbp
0x100000cc1:  movq   %rsp, %rbp
0x100000cc4:  movl   $0x0, %eax 0x100000cc9: movsd 0x247(%rip), %xmm0 0x100000cd1: movl$0x0, -0x4(%rbp)
0x100000cd8:  movsd  %xmm0, -0x10(%rbp)
0x100000cdd:  popq   %rbp
0x100000cde:  ret

Note: there is no instruction for callq, which means no function was called. Since exp(1.0) was evaluated at compile time it’s value was loaded in to register %xmm0 at line 5 and then loaded into a local variable (negative offset from %rbp register) at line 7. Life is a bit easy working with debug builds.

Had we not evaluated exp(z) on compile time, just remove constexpr from code in main:

 1 2  double exp1 = exp(1); // no constexpr - this will be evaluated at runtime 

generated assembly for main function is:

cpp-cxprmain at main.cpp:388:
0x1000009e0:  pushq  %rbp
0x1000009e1:  movq   %rsp, %rbp
0x1000009e4:  subq   $0x10, %rsp 0x1000009e8: movabsq$0x1, %rax
0x1000009f2:  cvtsi2sdq %rax, %xmm0
0x1000009f7:  movl   $0x0, -0x4(%rbp) 0x1000009fe: callq 0x100000b10 ; exp(double) at main.cpp:354 0x100000a03: movl$0x0, %eax
0x100000a08:  movsd  %xmm0, -0x10(%rbp)
0x100000a0d:  addq   $0x10, %rsp 0x100000a11: popq %rbp 0x100000a12: ret Here we are loading abs value 1 into register %rax (line 5), then converting int value to floating point with cvtsi2sdq instruction at line 6 and writing that in %xmm0 register. Then at line 8 we are calling exp(double) function which will again put the value in register %xmm0. This value is then moved to a local variable at line 10 (negative offset from %rbp register). The value computed at runtime is : 2.7182815255731922 Here is the assembly code generated for exp(double) function: cpp-cxprexp(double) at main.cpp:354: 0x100000b10: pushq %rbp 0x100000b11: movq %rsp, %rbp 0x100000b14: subq$0x90, %rsp
0x100000b1b:  movl   $0x2, %edi 0x100000b20: movabsq$0x1, %rax
0x100000b2a:  cvtsi2sdq %rax, %xmm1
0x100000b2f:  movsd  %xmm0, -0x8(%rbp)
0x100000b39:  movsd  -0x8(%rbp), %xmm0
0x100000b3e:  movsd  %xmm1, -0x10(%rbp)
0x100000b43:  callq  0x100000d20           ; pow(double, int) at main.cpp:344
0x100000b48:  movl   $0x2, %edi 0x100000b4d: movsd %xmm0, -0x18(%rbp) 0x100000b52: callq 0x100000d90 ; factorial(int) at main.cpp:349 0x100000b57: movl$0x3, %edi
...
...
; many more instructions to make 7 more calls to pow and factorial
...
...
;`