web-dev-qa-db-ja.com

intがshortにキャストされて切り捨てられた場合、新しい値はどのように決定されますか?

Cで整数がshortにキャストされたときに何が起こるかを誰かが明確にできますか? Raspberry Piを使用しているので、intは32ビットであるため、shortは16ビットでなければなりません。

たとえば、次のCコードを使用するとします。

int x = 0x1248642;
short sx = (short)x;
int y = sx;

xは切り捨てられますが、誰かが正確に説明できますか?シフトは使用されていますか?数値は32ビットから16ビットにどのくらい正確に切り捨てられますか?

33
Bolboa

ISO C標準によれば、整数を符号付き型に変換し、値がターゲット型の範囲外にある場合、結果は実装定義です。 (または、実装定義のシグナルを生成できますが、これを行うコンパイラーは知りません。)

実際には、最も一般的な動作は、上位ビットが破棄されることです。したがって、intが32ビットでshortが16ビットであると仮定すると、値0x1248642を変換すると、おそらく0x8642のようなビットパターンが生成されます。 (ほとんどすべてのシステムで使用される)符号付き型の2の補数表現を想定すると、高位ビットは符号ビットであるため、結果の数値は-31166になります。

int y   =   sx;

これには、shortからintへの暗黙的な変換も含まれます。 intの範囲は、少なくともshortの範囲全体をカバーすることが保証されているため、値は変更されません。 (あなたの例では、sxの値はたまたま負であるため、この表現の変更にはsign extensionが含まれる可能性が高く、1符号ビットをすべてに伝播します。結果の上位16ビット。)

前述したように、これらの詳細は言語標準では必要ありません。値をより狭い型に切り捨てたい場合は、おそらく次のように、符号なしの型(言語指定のラップアラウンド動作があります)およびおそらく明示的なマスキング操作を使用するのが最善です。

unsigned int x = 0x1248642;
unsigned short sx = x & 0xFFFF;

16ビット変数に押し込みたい32ビットの量がある場合、最初にすべきことは、値が収まらない場合のコードの動作を決定することです。それを決めたら、あなたが望むことをするCコードを書く方法を理解することができます。切り捨てが必要な場合もあります。その場合、特に符号なしの型を使用している場合は、タスクが簡単になります。範囲外の値はエラーである場合があります。その場合、その値を確認し、エラーの処理方法を決定する必要があります。場合によっては、値を切り捨てるのではなく飽和させたい場合があります。そのためには、コードを記述する必要があります。

Cで変換がどのように機能するかを知ることは重要ですが、その質問でstartを指定すると、間違った方向から問題に近づいている可能性があります。

38
Keith Thompson

32ビットの値は、16 cmの長さのパンに詰め込んだ場合、32 cmの長さのバナナパンが切り取られるのと同じ方法で16ビットに切り捨てられます。それの半分は収まり、それでもバナナのパンで、残りは「なくなって」しまいます。

11
Amit

切り捨ては、CPUレジスタで発生します。これらのサイズは異なります:8/16/32/64ビット。今、次のようなレジスタを想像できます。

<--rax----------------------------------------------------------------> (64-bit)
                                    <--eax----------------------------> (32-bit)
                                                      <--ax-----------> (16-bit)
                                                      <--ah--> <--al--> (8-bit high & low)
01100011 01100001 01110010 01110010 01111001 00100000 01101111 01101110

xには、最初に32ビット値0x1248642。メモリ*では、次のようになります。

-----------------------------
|  01  |  24  |  86  |  42  |
-----------------------------
 31..24 23..16 15..8  7..0       

これで、コンパイラーはxをレジスターにロードします。それから、最下位16ビット(つまり、ax)をロードし、sxに格納するだけです。


*エンディアンネスは、単純化のために考慮されていません

6
edmz

おそらく、コードはそれ自体を語らせます:

#include <stdio.h>

#define BYTETOBINARYPATTERN "%d%d%d%d%d%d%d%d"
#define BYTETOBINARY(byte)  \
   ((byte) & 0x80 ? 1 : 0), \
   ((byte) & 0x40 ? 1 : 0), \
   ((byte) & 0x20 ? 1 : 0), \
   ((byte) & 0x10 ? 1 : 0), \
   ((byte) & 0x08 ? 1 : 0), \
   ((byte) & 0x04 ? 1 : 0), \
   ((byte) & 0x02 ? 1 : 0), \
   ((byte) & 0x01 ? 1 : 0) 

int main()
{
    int x    =   0x1248642;
    short sx = (short) x;
    int y    =   sx;

    printf("%d\n", x);
    printf("%hu\n", sx);
    printf("%d\n", y);

    printf("x: "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN"\n",
        BYTETOBINARY(x>>24), BYTETOBINARY(x>>16), BYTETOBINARY(x>>8), BYTETOBINARY(x));

    printf("sx: "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN"\n",
        BYTETOBINARY(y>>8), BYTETOBINARY(y));

    printf("y: "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN" "BYTETOBINARYPATTERN"\n",
        BYTETOBINARY(y>>24), BYTETOBINARY(y>>16), BYTETOBINARY(y>>8), BYTETOBINARY(y));

    return 0;
}

出力:

19170882
34370
-31166

x: 00000001 00100100 10000110 01000010
sx: 10000110 01000010
y: 11111111 11111111 10000110 01000010

ご覧のとおり、int-> shortは期待どおり下位16ビットを生成します。

shortintにキャストすると、上位16ビットが設定されたshortが生成されます。ただし、これは実装固有の未定義の動作であると思われます。あなたは本質的に16ビットのメモリを整数として解釈し、そこにあるゴミの余分な16ビットを読み取ります(コンパイラがニースでバグをより迅速に見つけたい場合は1です)。

Ithink以下を実行しても安全であるはずです:

int y = 0x0000FFFF & sx;

明らかに、失われたビットを取り戻すことはできませんが、これにより、上位ビットが適切にゼロ化されることが保証されます。

誰もが信頼できる参照でshort-> int high bit動作を検証できる場合、それは高く評価されるでしょう。

注: this answer から適応されたバイナリマクロ。

4
Dan Bechard

単に上位16ビットが整数から切り捨てられます。したがって、あなたのショートは0x8642これは実際には負の数です-31166

sx値は、xの最下位2バイトと同じになります。この場合、0x8642になり(16ビット符号付き整数として解釈される場合)、10進数で-31166になります。

2
nsilent22