2

固定小数点の正数を表す構造体を作成しました。小数点の両側の数値を 2 バイトで構成したい。

typedef struct Fixed_t {
    unsigned short floor; //left side of the decimal point
    unsigned short fraction; //right side of the decimal point
} Fixed;

ここで、2 つの固定小数点数と を加算Fixed xFixed yます。そのために、それらを整数のように扱い、加算します。

(Fixed) ( (int)x + (int)y );

Fixedしかし、Visual Studio 2010 コンパイラが言うように、 と の間で変換することはできませんint

これを行う正しい方法は何ですか?

{short floor, short fraction}編集: Fixedの実装にはコミットしていません。

4

9 に答える 9

6

厄介なハックを試みることもできますが、エンディアン性に問題があります。変換するために何をするにしても、コンパイラfloorは、結果の最も重要な部分と重要でない部分になりたいことをどのように認識しているfractionのでしょうか? メモリの再解釈に依存するソリューションは、あるエンディアンでは機能しますが、別のエンディアンでは機能しません。

次のいずれかを行う必要があります。

(1) 変換を明示的に定義します。short16 ビットと仮定すると、次のようになります。

unsigned int val = (x.floor << 16) + x.fraction;

(2) 2 つの short ではなくメンバーをFixed持つように変更し、必要に応じて構成するのではなく、必要に応じて分解します。int

足し算を高速にしたい場合は、(2) を実行する必要があります。64 ビット型の場合は、分解せずに乗算を行うこともできますunsigned int result = (((uint64_t)x) * y) >> 16

ちなみに、厄介なハックは次のとおりです。

unsigned int val;
assert(sizeof(Fixed) == sizeof(unsigned int))              // could be a static test
assert(2 * sizeof(unsigned short) == sizeof(unsigned int)) // could be a static test
memcpy(&val, &x, sizeof(unsigned int));

これは、Fixed にパディングがない (そして整数型にはパディング ビットがない) ビッグ エンディアン システムで機能します。リトルエンディアン システムでは、Fixed のメンバーを別の順序にする必要があります。これが厄介な理由です。memcpy を介したキャストが正しい場合もあります (この場合、これは「厄介なハック」ではなく「トリック」です)。これはちょうどそれらの時間の 1 つではありません。

于 2010-10-26T11:50:34.277 に答える
5

必要に応じてユニオンを使用できますが、エンディアンの問題に注意してください。算術演算が機能せず、確かに移植性がないことに気付くかもしれません。

typedef struct Fixed_t {
   union { 
        struct { unsigned short floor; unsigned short fraction }; 
        unsigned int whole;
         };
} Fixed;

これは、ビッグエンディアンで動作する可能性が高い(と思います)(Windows / Intelはそうではありません)。

于 2010-10-26T11:42:52.043 に答える
3

いくつかの魔法:

typedef union Fixed {
    uint16_t w[2];
    uint32_t d;
} Fixed;
#define Floor w[((Fixed){1}).d==1]
#define Fraction w[((Fixed){1}).d!=1]

キーポイント:

  • short固定サイズの整数型を使用しているため、16 ビットintか 32ビットかに依存しません。
  • とのマクロ(関数FloorとのFraction衝突を避けるために大文字になっています) は、とfloor()のように、エンディアンに依存しない方法で 2 つの部分にアクセスします。foo.Floorfoo.Fraction

編集: OPのリクエストで、マクロの説明:

共用体は、いくつかの異なる重複する型から構成されるオブジェクトを宣言する方法です。ここでは、uint16_t w[2];重複する がuint32_t d;あり、2 つの 16 ビット単位または 1 つの 32 ビット単位として値にアクセスできます。

(Fixed){1}複合リテラルであり、より冗長に のように書くことができます(Fixed){{1,0}}。その最初の要素 ( uint16_t w[2];) は で初期化され{1,0}ます。この式((Fixed){1}).dは、最初の 16 ビット半分が 1 で、2 番目の 16 ビット半分が 0 である 32 ビット整数に評価されます。リトル エンディアン システムでは、この値は 1 であるため((Fixed){1}).d==1、1 (真) と((Fixed){1}).d!=1評価され、次のように評価されます。 0 (偽)。ビッグ エンディアン システムでは、逆になります。

したがって、リトルエンディアン システムでは、Floorisw[1]およびFractionisw[0]です。ビッグエンディアン システムでは、Floorisw[0]およびFractionisw[1]です。いずれにせよ、プラットフォームのエンディアンに合わせて 32 ビット値の正しい半分を格納/アクセスすることになります。

理論的には、仮想システムは 16 ビット値と 32 ビット値に完全に異なる表現を使用し (たとえば、2 つの半分のビットをインターリーブする)、これらのマクロを壊す可能性があります。実際には、そうはなりません。:-)

于 2010-10-26T15:54:01.143 に答える
2

Fixedコンパイラはが と同じ量のスペースを使用することを保証しないため、これは移植可能ではありませんint。正しい方法は、関数を定義することFixed add(Fixed a, Fixed b)です。

于 2010-10-26T11:39:38.817 に答える
1

ピースを別々に追加するだけです。「1」を意味する分数の値を知る必要があります - ここではそれを呼んでいFRAC_MAXます:

 // c = a + b
 void fixed_add( Fixed* a, Fixed* b, Fixed* c){
     unsigned short carry = 0;
     if((int)(a->floor) + (int)(b->floor) > FRAC_MAX){
         carry = 1;
         c->fraction = a->floor + b->floor - FRAC_MAX; 
     }
     c->floor = a->floor + b->floor + carry;
 }

または、固定小数点を 2 バイト境界に設定するだけの場合は、次のようにすることができます。

void fixed_add( Fixed* a, Fixed *b, Fixed *c){
    int ia = a->floor << 16 + a->fraction;
    int ib = b->floor << 16 + b->fraction;
    int ic = ia + ib;
    c->floor = ic >> 16;
    c->fraction = ic - c->floor;
}
于 2010-10-26T11:44:41.390 に答える
0

これを試して:

typedef union {
    struct Fixed_t {
        unsigned short floor; //left side of the decimal point
        unsigned short fraction; //right side of the decimal point
    } Fixed;
    int Fixed_int;
}
于 2010-10-26T11:42:59.527 に答える
0

コンパイラが 2 つの short を 4 バイトに配置する場合、memcpy を使用して int を構造体にコピーできますが、別の回答で述べたように、これは移植性がなく、かなり醜いです。

各フィールドを個別のメソッドで個別に追加してもよろしいですか? パフォーマンス上の理由から整数を保持しますか?

于 2010-10-26T11:45:44.480 に答える
-1
// add two Fixed 
Fixed operator+( Fixed a, Fixed b ) 
{   
...
}

//add Fixed and int
Fixed operator+( Fixed a, int b ) 
{   
...
}
于 2010-10-26T12:00:58.370 に答える
-1

次を使用して、アドレス可能な型を別の型にキャストできます。

*(newtype *)&var
于 2010-10-26T14:53:03.480 に答える