2017-04-03 18 views
2

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ビットの場合、最後の3つの定数が大きすぎてフィットしないため、失敗します。

これを回避する1つの方法は、関数のインターフェイスとテーブルのタイプを両方ともuint32_tまたはuint64_tに変更することです。しかし、これを__builtin_clzl()labs()など、これらのタイプを使用しない既存のAPIとどう組み合わせることができますか?

もう1つの方法は、インターフェイスを同じに保つことですが、関数内の引数をサポートされる最大サイズuint64_tに昇格させ、このサイズのテーブル要素を保存したままにしておきます。しかし、それは32ビットシステムでは非常に非効率的です。

長い整数のサイズを定義するマクロを提供し、テーブルの2行目を#ifの内側に配置するように調整できます。 sizeof()はプリプロセッサでは使用できないため、これは困難です。サイズを決定して設定ヘッダーを生成するには、autoconfのようなものが必要です。これは、既存のビルドプロセスに適合するのが難しいです。

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に仮定しても安全になるという仮定を作り、:1で

が署名/署名のないintlong int、およびlong long int機能のフルセットを提供することを目的とする、ここでは別の方法ですlong long intは特定のサイズであり、long intはサイズがどちらかと同じになります。これは現在存在するほぼすべての32ビットプラットフォームでのケースです。

もっと良い方法がありますか?

答えて

3

は、この上記のアプローチは、「マクロ数学」とのトラブルを持っているん#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 
    }; 

のシリーズを考えてみましょうコードは合理的な仮定ことになり、以下のよう時々、(経験に基づいて、スペックではない)署名されています最大unsigned longは約2x最大signed longです。 "ネスト化"アプローチは、 "マクロ数学"が以前の成功に依存するように機能するので、より良い方法です。 -bits。これにより、古いコンパイラとの違いが大きくなるか、このスキームを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 

「マクロ数学」以上プリプロセッサ演算以前C89等を、C11(およびおそらくC99)少なくとも64ビットの数学を用いて行われるが、唯一の少なくとも32ビット。このトークン変換および評価の目的のため

それらはヘッダ<stdint.h>で定義されたタイプintmax_tuintmax_t、それぞれ、同じ表現を持つかのように、すべての符号付き整数型およびすべての符号なし整数タイプが作用する。)この文字定数の解釈が含まれます。これには、エスケープシーケンスを実行文字セットのメンバーに変換することが含まれます。 C11§6.10.14

+0

これは問題があるようです。実装の 'intmax_t'(または定数が符号なしであれば' uintmax_t')が関係式の右辺の定数を表現するのに十分な大きさでなければ、関係式にはないと思う傾向があります所望の効果。 –

+0

@JohnBollinger更新を参照してください。あなたの懸念は非常に有効です。ネストされたアプローチはそれに対処する必要があります。マクロの数学は最低でも32ビットで動作し、その後はうまくいくと確信できます。 – chux

+1

マクロの数値式 –

0

あなたは、テーブル要素(または同じものであってもなくてもよい)unsigned long long intまたはuintmax_tを入力与えることを検討できます。 C99またはC11に準拠する実装では、少なくとも64ビット幅のunsigned long long intが提供されるため、uintmax_tも少なくともその幅になります。

もちろん、あなたの関数が64ビットを超える入力をどのように扱うことができるのかという疑問が湧きます。

関連する問題