2017-01-18 5 views
2

私はこの世界の仕事を考えていたが、それはしません。負の数値:符号付き整数の符号ビットを0に変更するにはどうすればよいですか?

int a = -500; 
a = a << 1; 
a = (unsigned int)a >> 1; 
//printf("%d",a) gives me "2147483148" 

私の考えは、unsigned int型は、それがだと保証するように、左シフトはそう右シフトそれは、左端の符号ビットを削除するというものでした算術ではなく論理的なシフト。これはなぜ間違っているのですか?

また:

int a = -500; 
a = a << 1; 
//printf("%d",a) gives me "-1000" 
+0

なぜ 'ABS(a)は' ? – DyZ

+2

それは動作します、それは0に印を設定します。あなたがbitmathで 'abs'を実装したいのであれば、これより少し面倒です。 – harold

+0

変数をprintfすると、目的の500の代わりに2147483148が得られます。絶対値を符号なしintに格納しようとしているためabs(a)を使用できません。 -2,147,483,648、右? – Mike

答えて

7

TL; DR:最も簡単な方法は、<stdlib.h>からabs関数を使用することです。答えの残りの部分には、コンピュータ上の負の数の表現が含まれます。

負の整数は、ほとんどの場合、2's complement formで表されます。

(下記の注を参照)数の負を取得する方法である:

  1. を務めるそのMSBを除いて、(データ・タイプの先行ゼロを含む整数のバイナリ表現を取ります符号ビット)。
  2. 上記番号の1's complementになります。
  3. 1を1の補数に加算します。
  4. 接頭辞a sign bit。例として500を使用

  1. 500のバイナリ表現を取る:_000 0001 1111 0100_は、符号ビットのプレースホルダです)。
  2. それの1's-補数/逆を取る:_111 1110 0000 1011
  3. は1の補数に1を追加:_111 1110 0000 1011 + 1 = _111 1110 0000 1100を。これは、符号ビットを0に置き換えたときに得た2147483148と同じです。
  4. 接頭辞0は正の数を示し、1の負の数は1111 1110 0000 1100です。 (これは上の2147483148とは異なります。あなたが上記の値を得た理由は、あなたがMSBを噛んだためです。

記号の反転も同様のプロセスです。表示される大きな値につながる16ビットまたは32ビットの数値を使用すると、先行するものが得られます。各ケースでLSBは同じにする必要があります。

注:1の補数表示を持つマシンがありますが、それらは少数です。 0が同じ表現を有するので、2の補数が通常好ましい。すなわち、-0および0は、2の補数表記ですべて0と表される。

2

負の整数を左シフトすると、未定義のビヘイビアが呼び出されるため、そのようにすることはできません。 a = (unsigned int)a << 1;を実行した場合は、コードを使用している可能性があります。あなたは500 = 0xFFFFFE0C、左にシフトされた1 = 0xFFFFFC18を得ます。

a = (unsigned int)a >> 1;確かに論理的なシフトを保証するので、0x7FFFFE0Cが得られます。これは小数点2147483148です。

しかし、これは不必要に複雑です。 符号ビットを変更する最も便利な方法は、単にa = -aです。その他のコードやメソッドには疑問があります。あなたはしかし、ビットいじることを主張した場合

、あなたも

(int32_t)a & ~(1u << 31) 

これは、32ビットのシステムに移植され、(int32_t)以来の保証2の補数のような何かを行うことができますが、1u << 31は32ビットintタイプを想定しています。

デモ:予想通り

#include <stdio.h> 
#include <stdint.h> 

int main (void) 
{ 
    int a = -500; 
    a = (unsigned int)a << 1; 
    a = (unsigned int)a >> 1; 
    printf("%.8X = %d\n", a, a); 

    _Static_assert(sizeof(int)>=4, "Int must be at least 32 bits."); 
    a = -500; 
    a = (int32_t)a & ~(1u << 31); 
    printf("%.8X = %d\n", a, a); 

    return 0; 
} 
+1

あなたの例で31の左シフトはUBです。なぜなら2^31はint32_tで表現できないからです。 – 2501

+0

編集:intがint32_tと同じ表現を持つと仮定します。 – 2501

+0

@ 2501よろしくです。修正しました、ありがとうございます。 – Lundin

-1

あなたは1ビットのあなたの最初の左シフト後のあなたの「また」セクション、DOESに置くように-1000を反映しています。

問題は、キャストからunsigned intです。上で説明したように、負の数は2の補数で表され、符号が左端のビット(最上位ビット)によって決定されることを意味します。 unsigned intにキャストすると、その値は符号を表さなくなりますが、intがとりうる最大値は増加します。

32ビットのintを仮定すると、MSBは-2^31(= -2147483648)を表し、符号なし整数の2147483648は正の値を表し、2 * 2147483648 = 4294967296になります。 -1000あなたが4294966296.右シフトは2で、これを分割し、あなたはこれが役に立つかもしれ願って2147483148.

に到着し得る:(Print an int in binary representation using Cから印刷funcを修正)

void int2bin(int a, char *buffer, int buf_size) { 
    buffer += (buf_size - 1); 

    for (int i = buf_size-1; i >= 0; i--) { 
     *buffer-- = (a & 1) + '0'; 

     a >>= 1; 
    } 
} 

int main() { 
    int test = -500; 
    int bufSize = sizeof(int)*8 + 1; 
    char buf[bufSize]; 
    buf[bufSize-1] = '\0'; 
    int2bin(test, buf, bufSize-1); 
    printf("%i (%u): %s\n", test, (unsigned int)test, buf); 
    //Prints: -500 (4294966796): 11111111111111111111111000001100 


    test = test << 1; 
    int2bin(test, buf, bufSize-1); 
     printf("%i (%u): %s\n", test, (unsigned int)test, buf); 
    //Prints: -1000 (4294966296): 11111111111111111111110000011000 


    test = 500; 
    int2bin(test, buf, bufSize-1); 
    printf("%i (%u): %s\n", test, (unsigned int)test, buf); 
    //Prints: 500 (500): 00000000000000000000000111110100 


    return 0; 
} 
関連する問題