如何使用预处理器计算日志

Has*_*ien 4 c

如何在Windows上使用预处理器执行log(x)?

喜欢:

#define A    log(4)/log(2)
Run Code Online (Sandbox Code Playgroud)

在我的代码之后的数组

int b[A]; // A=2 will be computed with the preprocessor ! not in run time
Run Code Online (Sandbox Code Playgroud)

5go*_*der 7

好吧,现在为脏蛮力预处理器诡计.

根据你的问题,我假设你真正想要的不是一般对数(在整数运算中甚至不可能),而是表示给定数字所需的位数.如果我们将自己限制为32位整数,那么就有一个解决方案,尽管它并不漂亮.

#define IS_REPRESENTIBLE_IN_D_BITS(D, N)                \
  (((unsigned long) N >= (1UL << (D - 1)) && (unsigned long) N < (1UL << D)) ? D : -1)

#define BITS_TO_REPRESENT(N)                            \
  (N == 0 ? 1 : (31                                     \
                 + IS_REPRESENTIBLE_IN_D_BITS( 1, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS( 2, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS( 3, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS( 4, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS( 5, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS( 6, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS( 7, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS( 8, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS( 9, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(10, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(11, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(12, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(13, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(14, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(15, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(16, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(17, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(18, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(19, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(20, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(21, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(22, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(23, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(24, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(25, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(26, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(27, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(28, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(29, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(30, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(31, N)    \
                 + IS_REPRESENTIBLE_IN_D_BITS(32, N)    \
                 )                                      \
   )
Run Code Online (Sandbox Code Playgroud)

我们的想法是,一些Ñ > 0具有使用完全相同的表示d比特当且仅当Ñ ≥2 d -1Ñ <2 d.在特别处理了n = 0的情况之后,我们只是对所有32个可能的答案进行暴力破解.

辅助宏IS_REPRESENTIBLE_IN_D_BITS(D, N)将扩展为一个表达式,用于计算Dif是否N可以使用精确D位来表示,-1否则.我已经定义了宏,如果答案为"否",则结果为-1.为了补偿负数,我最后加31.如果数字不能以任何1,...,32位表示,那么整体结果将为-1,这将有助于我们捕获一些错误.

表达式BITS_TO_REPRESENT(42)是一个有效的编译时常量,用于数组长度声明.

总而言之,总是使你的阵列长32个元素的额外成本似乎可以被许多应用程序接受,它可以为你省去一些麻烦.所以,如果我真的不得不这样,我只会使用这种技巧.

更新:只是为了避免混淆:该解决方案并没有使用预处理程序,以评估"对数".所有预处理器都执行文本替换,如果使用-E开关进行编译,您可以看到(至少对于GCC).我们来看看这段代码:

int
main()
{
  int digits[BITS_TO_REPRESENT(42)];
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

它将被预处理(被警告):

int
main()
{
  int digits[(42 == 0 ? 1 : (31 + (((unsigned long) 42 >= (1UL << (1 - 1)) && (unsigned long) 42 < (1UL << 1)) ? 1 : -1) + (((unsigned long) 42 >= (1UL << (2 - 1)) && (unsigned long) 42 < (1UL << 2)) ? 2 : -1) + (((unsigned long) 42 >= (1UL << (3 - 1)) && (unsigned long) 42 < (1UL << 3)) ? 3 : -1) + (((unsigned long) 42 >= (1UL << (4 - 1)) && (unsigned long) 42 < (1UL << 4)) ? 4 : -1) + (((unsigned long) 42 >= (1UL << (5 - 1)) && (unsigned long) 42 < (1UL << 5)) ? 5 : -1) + (((unsigned long) 42 >= (1UL << (6 - 1)) && (unsigned long) 42 < (1UL << 6)) ? 6 : -1) + (((unsigned long) 42 >= (1UL << (7 - 1)) && (unsigned long) 42 < (1UL << 7)) ? 7 : -1) + (((unsigned long) 42 >= (1UL << (8 - 1)) && (unsigned long) 42 < (1UL << 8)) ? 8 : -1) + (((unsigned long) 42 >= (1UL << (9 - 1)) && (unsigned long) 42 < (1UL << 9)) ? 9 : -1) + (((unsigned long) 42 >= (1UL << (10 - 1)) && (unsigned long) 42 < (1UL << 10)) ? 10 : -1) + (((unsigned long) 42 >= (1UL << (11 - 1)) && (unsigned long) 42 < (1UL << 11)) ? 11 : -1) + (((unsigned long) 42 >= (1UL << (12 - 1)) && (unsigned long) 42 < (1UL << 12)) ? 12 : -1) + (((unsigned long) 42 >= (1UL << (13 - 1)) && (unsigned long) 42 < (1UL << 13)) ? 13 : -1) + (((unsigned long) 42 >= (1UL << (14 - 1)) && (unsigned long) 42 < (1UL << 14)) ? 14 : -1) + (((unsigned long) 42 >= (1UL << (15 - 1)) && (unsigned long) 42 < (1UL << 15)) ? 15 : -1) + (((unsigned long) 42 >= (1UL << (16 - 1)) && (unsigned long) 42 < (1UL << 16)) ? 16 : -1) + (((unsigned long) 42 >= (1UL << (17 - 1)) && (unsigned long) 42 < (1UL << 17)) ? 17 : -1) + (((unsigned long) 42 >= (1UL << (18 - 1)) && (unsigned long) 42 < (1UL << 18)) ? 18 : -1) + (((unsigned long) 42 >= (1UL << (19 - 1)) && (unsigned long) 42 < (1UL << 19)) ? 19 : -1) + (((unsigned long) 42 >= (1UL << (20 - 1)) && (unsigned long) 42 < (1UL << 20)) ? 20 : -1) + (((unsigned long) 42 >= (1UL << (21 - 1)) && (unsigned long) 42 < (1UL << 21)) ? 21 : -1) + (((unsigned long) 42 >= (1UL << (22 - 1)) && (unsigned long) 42 < (1UL << 22)) ? 22 : -1) + (((unsigned long) 42 >= (1UL << (23 - 1)) && (unsigned long) 42 < (1UL << 23)) ? 23 : -1) + (((unsigned long) 42 >= (1UL << (24 - 1)) && (unsigned long) 42 < (1UL << 24)) ? 24 : -1) + (((unsigned long) 42 >= (1UL << (25 - 1)) && (unsigned long) 42 < (1UL << 25)) ? 25 : -1) + (((unsigned long) 42 >= (1UL << (26 - 1)) && (unsigned long) 42 < (1UL << 26)) ? 26 : -1) + (((unsigned long) 42 >= (1UL << (27 - 1)) && (unsigned long) 42 < (1UL << 27)) ? 27 : -1) + (((unsigned long) 42 >= (1UL << (28 - 1)) && (unsigned long) 42 < (1UL << 28)) ? 28 : -1) + (((unsigned long) 42 >= (1UL << (29 - 1)) && (unsigned long) 42 < (1UL << 29)) ? 29 : -1) + (((unsigned long) 42 >= (1UL << (30 - 1)) && (unsigned long) 42 < (1UL << 30)) ? 30 : -1) + (((unsigned long) 42 >= (1UL << (31 - 1)) && (unsigned long) 42 < (1UL << 31)) ? 31 : -1) + (((unsigned long) 42 >= (1UL << (32 - 1)) && (unsigned long) 42 < (1UL << 32)) ? 32 : -1) ) )];
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

这看起来很糟糕,如果在运行时进行评估,那将是相当多的指令.但是,由于所有操作数都是常量(或文字,确切地说),编译器能够在编译时对其进行评估.它必须这样做,因为数组长度声明必须是C 89中的常量.

如果在其他不需要编译时常量的地方使用宏,则由编译器决定是否计算表达式.但是,如果启用了优化,则应该期望任何合理的编译器执行这种相当基本的优化 - 称为常量折叠.如果有疑问 - 一如既往 - 请查看生成的汇编代码.

例如,让我们考虑一下这个程序.

int
main()
{
  return BITS_TO_REPRESENT(42);
}
Run Code Online (Sandbox Code Playgroud)

return声明中的表达式显然不需要是编译时常量,所以让我们看看GCC将生成什么代码.(我正在使用-S开关在装配阶段停止.)

即使没有启用任何优化,我也会得到以下汇编代码,表明宏扩展已经折叠成常量6.

main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $6, %eax  # See the constant 6?
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
Run Code Online (Sandbox Code Playgroud)


Mar*_*ian 6

LOG使用最多 32 位整数的宏的更短的定义可能是:

#define LOG_1(n) (((n) >= 2) ? 1 : 0)
#define LOG_2(n) (((n) >= 1<<2) ? (2 + LOG_1((n)>>2)) : LOG_1(n))
#define LOG_4(n) (((n) >= 1<<4) ? (4 + LOG_2((n)>>4)) : LOG_2(n))
#define LOG_8(n) (((n) >= 1<<8) ? (8 + LOG_4((n)>>8)) : LOG_4(n))
#define LOG(n)   (((n) >= 1<<16) ? (16 + LOG_8((n)>>16)) : LOG_8(n))
Run Code Online (Sandbox Code Playgroud)

但是,在使用它之前,请检查您是否真的需要它。人们通常需要对 2 的幂的值使用对数。例如,在实现位数组时。虽然log作为常量表达式很难计算,但定义 2 的幂很容易。因此,您可以考虑将常量定义为:

#define logA   4
#define A      (1<<logA)
Run Code Online (Sandbox Code Playgroud)

代替:

#define A     16
#define logA  LOG(A)
Run Code Online (Sandbox Code Playgroud)