查找表,其内容取决于整数类型的大小

问题描述:

假设应该可移植到LP64和ILP32系统的功能,即long int可以是32位或64位。这个函数会有一些常量表,但常量本身需要基于类型的宽度。一个人为的例子:查找表,其内容取决于整数类型的大小

// Find largest power of 1000 less than x, aka log base 1000 rounded down to an integer 
unsigned long int intlog1000l(unsigned long int x) { 
    const unsigned long int powers[] = { 
     0, 1000, 1000000, 1000000000, 
     1000000000000, 1000000000000000, 1000000000000000000 }; 
    unsigned int i; 
    for (i = 0; i < sizeof(powers)/sizeof(*powers); i++) 
     if (powers[i] > x) break; 
    return i - 1; 
}  

如果long int是64位,那么这个代码按预期工作。但是如果long int是32位,它将会失败,因为最后三个常量太大而不适合。

解决此问题的一种方法是更改​​函数的接口和表的类型,以使用uint32_tuint64_t。但是,如何将这与不使用这些类型的现有API结合起来,如__builtin_clzl()labs()

另一种方法是保持接口相同,但将函数内部的参数提升为将支持的最大尺寸uint64_t,并将表格元素保存为此大小。但是在32位系统上这是非常低效的。

可以安排提供一个宏来定义一个长整数的大小,然后将表的第二行放在#if之内。这很难,因为sizeof()不适用于预处理器。需要像autoconf这样的东西来确定大小并生成一个配置头文件。这很难适应现有的构建过程。

在一个打算提供全套的符号/无符号intlong intlong long int功能,这里是另一种方式:

unsigned int intlog1000(unsigned int); // assume 32 bits 
unsigned long long int intlog1000ll(unsigned long long int); // assume 64 bits 
static inline unsigned long int intlog1000l(unsigned long int x) 
{ sizeof(x) == sizeof(unsigned int) ? intlog1000(x) : intlog1000ll(x); } 

此作一个假设,这将是安全的假设为int和long long int是一定的大小,long int的大小与其中一个相等。目前存在的几乎每个32位平台都是如此。

有没有更好的方法?

考虑一系列#if

#include <limits.h> 
const unsigned long int powers[] = { 
    0, 1000, 1000000, 1000000000 
    #if ULONG_MAX/1000 >= 1000000000 
     , 1000000000000u 
    #endif 
    #if ULONG_MAX/1000 >= 1000000000000 
     , 1000000000000000u 
    #endif 
    #if ULONG_MAX/1000 >= 1000000000000000 
     , 1000000000000000000u 
    #endif 
    }; 

这上面的方法确实有困难的“宏观数学”签订有时(根据经验,不符合规范),所以下面的代码做出合理的假设“嵌套”方法更好,因为它可以确保“宏观数学”的工作取决于先前的成功,对于C99,11编译器来说这并不重要,因为数学至少为64这比较老的编译器或者如果有人想把这个方案扩展到比64-b更宽的地方,这会产生更多的差异它unsigned long

#if LONG_MAX/1000 >= 500000000 
     , 1000000000000u 
     #if LONG_MAX/1000 >= 500000000000 
     , 1000000000000000u 
     #if LONG_MAX/1000 >= 5000000000000000 
      , 1000000000000000000u 
      #if LONG_MAX/1000 >= 5000000000000000000 
      #error powers[] needs extending 
      #endif 
     #endif 
     #endif 
    #endif 

“宏数学”或更好预处理器算术,与在C11至少64位数学(和可能C99)完成,但仅仅至少32位与早期像C89。

对于此令牌转换和评价,所有符号整数类型和所有无符号整数类型的目的充当如果它们具有相同的表示为分别的类型intmax_tuintmax_t在头<stdint.h>定义。)此包括解释字符常量,这可能涉及将转义序列转换为执行字符集成员。 C11§6.10.14

+0

这似乎有问题。如果实现的'intmax_t'(或者'uintmax_t',如果常量是无符号的)不足以代表关系表达式右边的常量,那么我倾向于认为关系表达式不会有期望的效果。 –

+0

@JohnBollinger查看更新。你的关心是非常有效的。嵌套的方法应该解决它。我们可以确信,宏数学运算至少32位有符号,然后继续工作。 – chux

+1

您可以在宏数学中使用整数后缀 –

你可以考虑给表元素类型unsigned long long intuintmax_t(这可能是也可能不是同样的事情)。任何符合C99或C11的实现将提供至少64位宽的unsigned long long int,因此uintmax_t也将至少那么宽。

当然,这引出了你的函数如何处理大于64位的输入的问题。