Engineer
Engineer

Reputation: 8857

Number and its logarithm as compile constants without manually changing both?

I have a constant WIDTH which is always a power of 2, e.g. #define WIDTH 1024.

I have an associated #define WIDTH_BINARY_LOG 10 which I must change every time that I modify WIDTH, to be the binary logarithm of WIDTH. This is undesirable as sometimes I forget to do so, and things consequently go haywire. Unfortunately pow() is also not available in macros.

Usage is

*z = zx >> WIDTH_BINARY_LOG.

My plan to fix this, given that the maximum possible value of WIDTH_BINARY_LOG is 16, was the following usage:

*z = zx >> BINARY_LOG(WIDTH)

with the following defines:

#define BINARY_LOG_1        0
#define BINARY_LOG_2        1
#define BINARY_LOG_4        2
#define BINARY_LOG_8        3
#define BINARY_LOG_16       4
#define BINARY_LOG_32       5
#define BINARY_LOG_64       6
#define BINARY_LOG_128      7
#define BINARY_LOG_256      8
#define BINARY_LOG_512      9
#define BINARY_LOG_1024     10
#define BINARY_LOG_2048     11
#define BINARY_LOG_4096     12
#define BINARY_LOG_8192     13
#define BINARY_LOG_16384    14
#define BINARY_LOG_32768    15
#define BINARY_LOG_65536    16

#define BINARY_LOG(n)       BINARY_LOG_##n

or

#define BINARY_LOG(n)       BINARY_LOG_#n

However, gcc complains bitterly in each case. What am I doing wrong? How would others tackle this problem, assuming the way above is completely unworthy?

Upvotes: 2

Views: 71

Answers (2)

Slizzered
Slizzered

Reputation: 899

Since there are already working answers available, I want to point to some different solution:

Using constexpr (C++11) that can be evaluated at compile-time to calculate WIDTH from a given exponent. This looks much nicer than the use of macros IMHO: (see Casey's answer in c++11 fast constexpr integer powers)

#include <cstdint>

constexpr int64_t ipow(int64_t base, int exp, int64_t result = 1) {
  return exp < 1 ? result : ipow(base*base, exp/2, (exp % 2) ? result*base : result);
}

int64_t foo(int64_t base, int exp) {
  return ipow(base, exp);
}

Upvotes: -2

Radek
Radek

Reputation: 846

It can be done with macros. I used this answer in my code:

#define WIDTH 2048

#define BINARY_LOG_1        0
#define BINARY_LOG_2        1
#define BINARY_LOG_4        2
#define BINARY_LOG_8        3
#define BINARY_LOG_16       4
#define BINARY_LOG_32       5
#define BINARY_LOG_64       6
#define BINARY_LOG_128      7
#define BINARY_LOG_256      8
#define BINARY_LOG_512      9
#define BINARY_LOG_1024     10
#define BINARY_LOG_2048     11
#define BINARY_LOG_4096     12
#define BINARY_LOG_8192     13
#define BINARY_LOG_16384    14
#define BINARY_LOG_32768    15
#define BINARY_LOG_65536    16

#define PPCAT_NX(A, B) A ## B
#define BINARY_LOG(B) PPCAT_NX(BINARY_LOG_, B)

BINARY_LOG(WIDTH)

Output of gcc -E test.c (only macro substitution phase):

>gcc -E test.c
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "test.c"
# 24 "test.c"
11

Upvotes: 3

Related Questions