12

Is it possible to compute pow(10,x) at compile time?

I've got a processor without floating point support and slow integer division. I'm trying to perform as many calculations as possible at compile time. I can dramatically speed up one particular function if I pass both x and C/pow(10,x) as arguments (x and C are always constant integers, but they are different constants for each call). I'm wondering if I can make these function calls less error prone by introducing a macro which does the 1/pow(10,x) automatically, instead of forcing the programmer to calculate it?

Is there a pre-processor trick? Can I force the compiler optimize out the library call?

AShelly
  • 34,686
  • 15
  • 91
  • 152
  • I believe I've seen proof that the C preprocessor is turing complete (I think it was a tape machine implemented in an obfuscated C contest in the preprocessor.) So there's a way. Don't know what that way is, though. – Greg D Jun 30 '09 at 21:15
  • Preprocessor #defines can't be recursive, since they are just text replacements. So like Greg, here's a place NOT to spend your time looking. :) – Michael Bray Jun 30 '09 at 21:23
  • 2
    @Greg D: However, starting with a Turing machine and implementing an exponent of 10 function strikes me as ambitious. – David Thornley Jun 30 '09 at 21:25

10 Answers10

21

There are very few values possible before you overflow int (or even long). For clarities sake, make it a table!

edit: If you are using floats (looks like you are), then no it's not going to be possible to call the pow() function at compile time without actually writing code that runs in the make process and outputs the values to a file (such as a header file) which is then compiled.

Bill K
  • 62,186
  • 18
  • 105
  • 157
  • 1
    Inspired! For powers of a known base. That aren't decimals. :) – Michael Bray Jun 30 '09 at 21:24
  • C and x are related in such a way that we won't overflow: given a value v, x is chosen so that 0.1 <= v/pow(10,x) < 1, and C is set to 32768*v. – AShelly Jun 30 '09 at 21:30
  • what you meant to say is `total = (total<<1) + (total<<3)` And most of compiler can do that automatically when you use `total *= 10` – leiz Jun 30 '09 at 21:49
  • @leiz - what? What I meant is that C is a 1.15 fixed-point representation of a number between 0.1 and 1. x is always an integer. – AShelly Jun 30 '09 at 22:02
  • 1
    There is no need to use a table. For integers you can use the E notation, e.g. 1.0E5. – quinmars Jul 02 '09 at 07:33
  • @quinmars, that comment is exactly the answer I was looking for, it just took me this long to realize it. `#define P10(x) (1e##x)` does exactly what I need. Make your comment an answer for a quick 25 pts. – AShelly Sep 28 '11 at 04:34
20

GCC will do this at a sufficiently high optimization level (-O1 does it for me). For example:

#include <math.h>

int test() {
        double x = pow(10, 4);
        return (int)x;
}

Compiles at -O1 -m32 to:

        .file   "test.c"
        .text
.globl test
        .type   test, @function
test:
        pushl   %ebp
        movl    %esp, %ebp
        movl    $10000, %eax
        popl    %ebp
        ret
        .size   test, .-test
        .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
        .section        .note.GNU-stack,"",@progbits

This works without the cast as well - of course, you do get a floating-point load instruction in there, as the Linux ABI passes floating point return values in FPU registers.

Sadeq Dousti
  • 3,346
  • 6
  • 35
  • 53
bdonlan
  • 224,562
  • 31
  • 268
  • 324
  • 3
    Nice. I'm wondering how they check if pow is a pure function or not in a reasonable compile time. Maybe they have a list of known functions that are so? – akappa Jun 30 '09 at 21:29
  • some of the math functions are most likely compiler built-in; also, GCC has a non-standard `pure` attribute for functions – Christoph Jun 30 '09 at 21:59
  • 'pure' isn't sufficient for cross-compile unit optimization of this kind; and GCC will perform constant folding after inlining if they're in the same compilation unit. pure is mostly just a hint to the compiler that it doesn't need to invalidate the data in its registers. – bdonlan Jun 30 '09 at 23:41
  • I hate to get into lexical parsers of c code but you can in theory write your own preprocessor to have exact control of optimizations. – ojblass Jul 01 '09 at 00:25
  • Not really a preprocessor so much as a precompiler - you'd have to deal with the possibility of local function pointers shadowing the global pow() etc – bdonlan Jul 01 '09 at 02:06
  • This is the best answer. – Hello Goodbye Dec 17 '20 at 15:02
11

You can do it with Boost.Preprocessor:

http://www.boost.org/doc/libs/1_39_0/libs/preprocessor/doc/index.html

Code:

#include <boost/preprocessor/repeat.hpp>

#define _TIMES_10(z, n, data) * 10
#define POW_10(n) (1 BOOST_PP_REPEAT(n, _TIMES_10, _))

int test[4] = {POW_10(0), POW_10(1), POW_10(2), POW_10(3)};
e.tadeu
  • 5,024
  • 2
  • 20
  • 21
9

Actually, by exploiting the C preprocessor, you can get it to compute C pow(10, x) for any real C and integral x. Observe that, as @quinmars noted, C allows you to use scientific syntax to express numerical constants:

#define myexp 1.602E-19   // == 1.602 * pow(10, -19)

to be used for constants. With this in mind, and a bit of cleverness, we can construct a preprocessor macro that takes C and x and combine them into an exponentiation token:

#define EXP2(a, b) a ## b
#define EXP(a, b) EXP2(a ## e,b)
#define CONSTPOW(C,x) EXP(C, x)

This can now be used as a constant numerical value:

const int myint = CONSTPOW(3, 4); // == 30000
const double myfloat = CONSTPOW(M_PI, -2); // == 0.03141592653
Jon Gjengset
  • 4,078
  • 3
  • 30
  • 43
8

You can use the scientific notation for floating point values which is part of the C language. It looks like that:

e = 1.602E-19   // == 1.602 * pow(10, -19)

The number before the E ( the E maybe capital or small 1.602e-19) is the fraction part where as the (signed) digit sequence after the E is the exponent part. By default the number is of the type double, but you can attach a floating point suffix (f, F, l or L) if you need a float or a long double.

I would not recommend to pack this semantic into a macro:

  1. It will not work for variables, floating point values, etc.
  2. The scientific notation is more readable.
quinmars
  • 11,175
  • 8
  • 32
  • 41
  • 5
    Although you don't recommend it, this is exactly what I need: `#define P10(X) (1eX)`, combined with `#define fixedpt(value,digits) ((value)*(1<<15)/P10(digits))` gives me the result I wanted, with no reliance on optimization settings. – AShelly Oct 17 '11 at 14:39
  • 1
    Which compiler worked with `#define P10(X) (1eX)`? In the Arduino compiler, I needed `#define P10(X) (1e##X)`. – BigBobby Sep 17 '15 at 22:21
4

Actually, you have M4 which is a pre-processor way more powerful than the GCC’s. A main difference between those two is GCC’s is not recursive whereas M4 is. It makes possible things like doing arithmetic at compile-time (and much more!). The below code sample is what you would like to do, isn’t it? I made it bulky in a one-file source; but I usually put M4's macro definitions in separate files and tune my Makefile rules. This way, your code is kept from ugly intrusive M4 definitions into the C source code I've done here.

$ cat foo.c
define(M4_POW_AUX, `ifelse($2, 1, $1, `eval($1 * M4_POW_AUX($1, decr($2)))')')dnl
define(M4_POW, `ifelse($2, 0, 1, `M4_POW_AUX($1, $2)')')dnl

#include <stdio.h>

int                     main(void)
{
  printf("2^0 = %d\n", M4_POW(2, 0));
  printf("2^1 = %d\n", M4_POW(2, 1));
  printf("2^4 = %d\n", M4_POW(2, 4));

  return 0;
}

The command line to compile this code sample uses the ability of GCC and M4 to read from the standard input.

$ cat foo.c | m4 - | gcc -x c -o m4_pow -
$ ./m4_pow
2^0 = 1
2^1 = 2
2^4 = 16

Hope this help!

Benny
  • 4,095
  • 1
  • 26
  • 27
4

If you just need to use the value at compile time, use the scientific notation like 1e2 for pow(10, 2)

If you want to populate the values at compile time and then use them later at runtime then simply use a lookup table because there are only 23 different powers of 10 that are exactly representable in double precision

double POW10[] = {1., 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10,
1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};

You can get larger powers of 10 at runtime from the above lookup table to quickly get the result without needing to multiply by 10 again and again, but the result is just a value close to a power of 10 like when you use 10eX with X > 22

double pow10(int x)
{
   if (x > 22)
      return POW10[22] * pow10(x - 22);
   else if (x >= 0)
      return POW10[x];
    else
        return 1/pow10(-x);
}

If negative exponents is not needed then the final branch can be removed.

You can also reduce the lookup table size further if memory is a constraint. For example by storing only even powers of 10 and multiply by 10 when the exponent is odd, the table size is now only a half.

phuclv
  • 37,963
  • 15
  • 156
  • 475
3

Recent versions of GCC ( around 4.3 ) added the ability to use GMP and MPFR to do some compile-time optimizations by evaluating more complex functions that are constant. That approach leaves your code simple and portable, and trust the compiler to do the heavy lifting.

Of course, there are limits to what it can do. Here's a link to the description in the changelog, which includes a list of functions that are supported by this. 'pow' is one them.

Chris Arguin
  • 11,850
  • 4
  • 34
  • 50
0

Unfortunately, you can't use the preprocessor to precalculate library calls. If x is integral you could write your own function, but if it's a floating-point type I don't see any good way to do this.

David Thornley
  • 56,304
  • 9
  • 91
  • 158
0

bdonlan's replay is spot on but keep in mind that you can perform nearly any optimization you chose on the compile box provided you are willing to parse and analyze the code in your own custom preprocessor. It is a trivial task in most version of unix to override the implicit rules that call the compiler to call a custom step of your own before it hits the compiler.

ojblass
  • 21,146
  • 22
  • 83
  • 132