if (i & 0x80000000) x = 1; else x = 0;

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
hands
記事: 14
登録日時: 7年前

if (i & 0x80000000) x = 1; else x = 0;

#1

投稿記事 by hands » 7年前

引数として受け取ったビットを左に回転した上で、結果を返すオーバーロード関数rotate()を作成しなさい。整数と長整数を受け取るように、この関数をオーバーロードしなさい。【監修者注:現在のほとんどすべてのコンパイラでは、整数と長整数を区別して実装されていません。したがってこの問題には、どちらも同じように解答するべきです。】

 という問題があり、ビットを回転する処理の仕方が分からなかったので解答(以下)

コード:

#include <iostream>
using namespace std;

int rotate(int i);
long rotate(long i);

int main()
{
	int a;
	long b;

	a = 0x8000;
	b = 8;
	cout << rotate(a);
	cout << "\n";
	cout << rotate(b);

	return 0;
}

int rotate(int i)
{
	int x;

	if (i & 0x80000000) x = 1;
	else x = 0;

	i = i << 1;
	i += x; 

	return i;
}

long rotate(long i)
{
	int x;

	if (i & 0x80000000) x = 1;
	else x = 0;

	i = i << 1;
	i += x;

	return i;
}
を確認し、私の環境windows10 Pro 64bit , Visual Studio Community 2017 において
65536
16
という結果が得られました。

int aの例では値 16進数 0x8000 は、2進数では 0000 0000 0000 0000 1000 0000 0000 0000 なので、
1が左へ1ビット移動し、32ビット目の? 0が一番右に入れられた結果、
0000 0000 0000 0001 0000 0000 0000 0000 になり、10進数表記で65536 と出力された。…ということのようです。(sizeof演算子で調べたらint型もlong型もともに4バイトでした。)

rotate()関数の宣言内

コード:

{
	int x;

	if (i & 0x80000000) x = 1;
	else x = 0;

	i = i << 1;
	i += x; 

	return i;
}
が、具体的にはどういう処理なのかが掴めないで困っております。
どの部分はどういう意味という風に教えていただけないでしょうか。
特にifの行がよく分かりません。

かずま

Re: if (i & 0x80000000) x = 1; else x = 0;

#2

投稿記事 by かずま » 7年前

hands さんが書きました:
7年前
が、具体的にはどういう処理なのかが掴めないで困っております。
どの部分はどういう意味という風に教えていただけないでしょうか。
特にifの行がよく分かりません。

コード:

int rotate(int i)
{
	int x;

	if (i & 0x80000000) x = 1;  // i の最上位ビットが 1 なら、x は 1
	else x = 0;                 // そうでないなら、x は 0

	i = i << 1;                 // i を左に 1ビットシフトする
	i += x;                     // i の最下位ビットに x を入れる

	return i;                   // 結果を返す
}
int が 32ビットだとします。

0x80000000 は最上位ビットが 1 で残りが全部 0 です。

i & 0x80000000 を計算すると、
i の最上位ビットが 0 の場合、結果は 0 になり、
i の最上位ビットが 1 の場合、結果は 0x80000000 になります。

if文は、「if (式) 文1 else 文2」という構文で、
式の値が 0 でないとき、文1 を実行し、
式の値が 0 のとき、文2 を実行するので、
i の最上位ビットが x に設定されます。

i << 1 で i を左に 1ビットシフトすると、
i の元の最上位ビットは無くなります。
i の最下位ビットは 0 になります。
そこに、値が 0 または 1 である x を足すと、
i の最下位ビットが x の値になります。

別解いろいろ

コード:

int rotate(int i) { return (i << 1) | (i >> 31) & 1; }
int rotate(int i) { return (i << 1) | ((unsigned int)i >> 31); }
int rotate(int i) { return (i << 1) | (i < 0); }
31 と直接書くよりも、<climits> を #include して、
(sizeof(int) * CHAR_BIT - 1) と書いたほうが良いでしょう。

int を long に置き換えれば、long rotate(long i) になります。

かずま

Re: if (i & 0x80000000) x = 1; else x = 0;

#3

投稿記事 by かずま » 7年前

ビット演算 (&, |, <<. >>) を使わない解

コード:

int rotate(int i) { return i * 2 + i / 0x80000000u; }
long rotate(long i) { return i * 2 + i / 0x80000000ul; }
0x80000000 は unsigned だから、u や ul は不要ですが、念のため。
i * 2 は、i + i と書いても構いません。

かずま

Re: if (i & 0x80000000) x = 1; else x = 0;

#4

投稿記事 by かずま » 7年前

かずま さんが書きました:
7年前
i << 1 で i を左に 1ビットシフトすると、
i の元の最上位ビットは無くなります。
i の最下位ビットは 0 になります。
誤解を与える表現なので、次のように訂正します。

i << 1 で i の値を左に 1ビットシフトすると、
その値では、i の値の最上位ビットは無くなります。
その値の最下位ビットは 0 になります。

あるいは、次のように訂正することもできます。

i = i << 1 で i の値を左に 1ビットシフトしたものを i に代入すると、
i の元の最上位ビットは無くなります。
i の最下位ビットは 0 になります。

hands
記事: 14
登録日時: 7年前

Re: if (i & 0x80000000) x = 1; else x = 0;

#5

投稿記事 by hands » 7年前

かずまさん ありがとうございます。
かずま さんが書きました:
7年前
int が 32ビットだとします。

0x80000000 は最上位ビットが 1 で残りが全部 0 です。

i & 0x80000000 を計算すると、
i の最上位ビットが 0 の場合、結果は 0 になり、
i の最上位ビットが 1 の場合、結果は 0x80000000 になります。
詳しく教えてくださったのに、まだ十分に理解できておらず、申し訳ないです。

一瞬、分かった気がするのですが、次の瞬間泡のごとく消えてしまいます。

i の最上位ビットが、よく分かりません。

i が 0x100000000 の場合は、結果が 0 でした。すなわち、i の最上位ビットは 0 です。
i が 0x100000001 の場合は、結果が 0x80000000 でした。すなわち、i の最上位ビットは 1 です。
となりますが、最上位以下は全部 0 で、最上位ビットも 0 ならという理解でよろしいですか。
(0x80000000 の2進表記は、最上位以下は全部 0 なので 0 、さらに最上位が 1 でなければ 0 。)

ということは、16進数で 1 ~ 80000000 はすべて結果は 0x80000000 、0 か下8桁がすべて 0 なら、結果は 0 で合ってますでしょうか。
(0x100000000 (33ビット以上)で、下32ビットがすべて 0 なら、i & 0x80000000 の結果はすべて 0)

かずま

Re: if (i & 0x80000000) x = 1; else x = 0;

#6

投稿記事 by かずま » 7年前

hands さんが書きました:
7年前
i が 0x100000000 の場合は、結果が 0 でした。すなわち、i の最上位ビットは 0 です。
0x100000000 は 33ビットですよ。

コード:

二項演算子& は、C++ の規格書では「ビット積演算子」、
C の規格書では「ビット単位のAND演算子」となっています。

ビット単位で
   0 & 0 == 0
   0 & 1 == 0
   1 & 0 == 0
   1 & 1 == 1

複数ビットの場合

    10110101      0xb5      181
  & 11001100    & 0xcc    & 204
 ------------  --------  -------
    10000100      0x84      132

16進や 10進で考えても演算結果は分かりづらいでしょう。

-----------------------------------------------------
0000 0000 ... 0000 0000    0x00000000             0
0000 0000 ... 0000 0001    0x00000001             1
0000 0000 ... 0000 0010    0x00000002             2
  :
0111 1111 ... 1111 1110    0x7ffffffe    2147483646
0111 1111 ... 1111 1111    0x7fffffff    2147483647
1000 0000 ... 0000 0000    0x80000000   -2147483648
1000 0000 ... 0000 0001    0x80000001   -2147483647
  :
1111 1111 ... 1111 1110    0xfffffff0            -2
1111 1111 ... 1111 1111    0xffffffff            -1
-----------------------------------------------------

    0001 0010 ... 0111 1000      0x12345678
  & 1000 0000 ... 0000 0000    & 0x80000000
 ---------------------------  --------------
    0000 0000 ... 0000 0000      0x00000000

	1010 1011 ... 0000 0001      0xabcdef01
  & 1000 0000 ... 0000 0000    & 0x80000000
 ---------------------------  --------------
    1000 0000 ... 0000 0000      0x80000000
    
i & 0x80000000 の値は、
    i が 0x00000000 ~ 0x7fffffff のとき 0x00000000
    i が 0x80000000 ~ 0xffffffff のとき 0x80000000
となります。
 

hands
記事: 14
登録日時: 7年前

Re: if (i & 0x80000000) x = 1; else x = 0;

#7

投稿記事 by hands » 7年前

かずま さんが書きました:
7年前
0x100000000 は 33ビットですよ。
!! ...ですね。少し錯綜しておりました。シフトと回転も検証の過程で混同しておりました。

0x80000000     10000000000000000000000000000000
0xabcdef01     10101011110011011110111100000001
0x7fffffff      1111111111111111111111111111111
0xffffffff     11111111111111111111111111111111
0x100000000   100000000000000000000000000000000
ようやくスッキリしました。理解の悪い私にお付き合いくださりありがとうございました。

疲れも吹き飛びました。全くもって失礼な話ですが、途中よく分からないけど「そういうもの」として処理しようかとも思いました。分かるまで聞いて本当にとてもよかったと思っています。
 私も含めて入門者は、繰返し同じようなことを質問することに、「そんなことも分からないようじゃ、やめた方がいいよ」と言われるのではないかと心配だと思います。しかし今回お世話になったかずまさんを始め、みなさん大変親切に教えてくださるのでどうにかやれるかもという希望をもつことができます。
今後もみなさんのお手を煩わすかとおもいますが、どうかご指導のほどお願いいたします。

最後に
かずま さんが書きました: int rotate(int i) { return (i << 1) | (i >> 31) & 1; }
int rotate(int i) { return (i << 1) | ((unsigned int)i >> 31); }
int rotate(int i) { return (i << 1) | (i < 0); }
も試し、無事実行できましたのでご報告いたします。

コード:

#include <iostream>
#include <climits>
using namespace std;

int rotate(int i);
long rotate(long i);

int main()
{
	int a;
	long b;

	a = 0x8000;
	b = 8;
	cout << rotate(a);
	cout << "\n";
	cout << rotate(b);

	return 0;
}

int rotate(int i)
{
	int x;
	if (i & 0x80000000) x = 1;
	else x = 0;

	return (i << 1) | (i >> (sizeof(int) * CHAR_BIT - 1)) & 1;
	return (i << 1) | ((unsigned int)i >> (sizeof(int) * CHAR_BIT - 1));
	return (i << 1) | (i < 0);

	return i;
}

long rotate(long i)
{
	int x;
	if (i & 0x80000000) x = 1;
	else x = 0;

	return (i << 1) | (i >> (sizeof(long) * CHAR_BIT - 1)) & 1;
	return (i << 1) | ((unsigned long)i >> (sizeof(long) * CHAR_BIT - 1));
	return (i << 1) | (i < 0);

	return i;
}

かずま

Re: if (i & 0x80000000) x = 1; else x = 0;

#8

投稿記事 by かずま » 7年前

hands さんが書きました:
7年前
も試し、無事実行できましたのでご報告いたします。
その試し方は間違っています。

0 か 1 に値をセットした x は、その後使用されていません。
最初の return文で値が呼び出し元に返されるので、
その後の 3つの return文は実行されません。

sizeof(int) や CHAR_BIT を使ったのは、
sizeof(int) = 2 のコンパイラや、sizeof(long) = 8 の
コンパイラが存在するからです。

正しい試し方は次の通り。

その1

コード:

#include <iostream>
#include <climits>   // CHAR_BIT
using namespace std;

int rotate(int i);
long rotate(long i);

int main()
{
	int a;
	long b;

	a = 0x8000;
	b = 8;
	cout << rotate(a);
	cout << "\n";
	cout << rotate(b);

	return 0;
}

int rotate(int i) {
	return (i << 1) | (i >> (sizeof(int) * CHAR_BIT - 1)) & 1;
}

long rotate(long i)
{
	return (i << 1) | (i >> (sizeof(long) * CHAR_BIT - 1)) & 1;
}
その2

コード:

#include <iostream>
#include <climits>   // CHAR_BIT
using namespace std;

int rotate(int i);
long rotate(long i);

int main()
{
	int a;
	long b;

	a = 0x8000;
	b = 8;
	cout << rotate(a);
	cout << "\n";
	cout << rotate(b);

	return 0;
}

int rotate(int i) {
	return (i << 1) | ((unsigned int)i >> (sizeof(int) * CHAR_BIT - 1));
}

long rotate(long i)
{
	return (i << 1) | ((unsigned long)i >> (sizeof(long) * CHAR_BIT - 1));
}
その3

コード:

#include <iostream>
#include <climits>   // CHAR_BIT
using namespace std;

int rotate(int i);
long rotate(long i);

int main()
{
	int a;
	long b;

	a = 0x8000;
	b = 8;
	cout << rotate(a);
	cout << "\n";
	cout << rotate(b);

	return 0;
}

int rotate(int i) {
	return (i << 1) | (i < 0);
}

long rotate(long i)
{
	return (i << 1) | (i < 0);
}
その1 には最後に & 1 があるのに、
その2 には最後に & 1 がなくてよいのはなぜか
説明できますか?

その3 の (i < 0) の意味がわかりますか?

hands
記事: 14
登録日時: 7年前

Re: if (i & 0x80000000) x = 1; else x = 0;

#9

投稿記事 by hands » 7年前

かずま さんが書きました:
7年前

その試し方は間違っています。

0 か 1 に値をセットした x は、その後使用されていません。
最初の return文で値が呼び出し元に返されるので、
その後の 3つの return文は実行されません。
確かにこれではxが使われていませんね。
気が付きませんでした。
かずま さんが書きました:
7年前
その1 には最後に & 1 があるのに、
その2 には最後に & 1 がなくてよいのはなぜか
説明できますか?

その3 の (i < 0) の意味がわかりますか?
詳しくはわかりませんが予想としては、&1があるのはunsignedかsignedかという部分と関係があると思います。signed は負の値を扱う。
&1を取っても結果は同じでしたが、a, bがマイナスの値の場合は&1の有り無しで結果が異なりました。

その3 の (i < 0) の意味はよく分かりません。上で予想したのと同じようなことかと思っています。

よろしければ、解説を頂けるとありがたいです。

かずま

Re: if (i & 0x80000000) x = 1; else x = 0;

#10

投稿記事 by かずま » 7年前

コード:

#include <iostream>  // cout, endl, hex, dec
#include <iomanip>   // setfill, setw
using namespace std;

int main()
{
	int i;
	unsigned int u;

	cout << hex << setfill('0');  // 16進表示、詰め物文字 ' ' を '0' に変更

	i = u = 0x12345678;
	cout << "\nMSB = 0\n";
	cout << "  unsigned: " << u << " >> 16 == " << setw(8) << (u >> 16) << endl;
	cout << "  int     : " << i << " >> 16 == " << setw(8) << (i >> 16) << endl;
	cout << "  int     : " << i << " < 0 == " << (i < 0) << endl;

	i = u = 0xabcdef01;
	cout << "\nMSB = 1\n";
	cout << "  unsigned: " << u << " >> 16 == " << setw(8) << (u >> 16) << endl;
	cout << "  int     : " << i << " >> 16 == " << setw(8) << (i >> 16) << endl;
	cout << "  int     : " << i << " < 0 == " << (i < 0) << endl;

	cout << "\n0x" << i << " == " << dec << i << endl;
}
実行結果

コード:


MSB = 0
  unsigned: 12345678 >> 16 == 00001234
  int     : 12345678 >> 16 == 00001234
  int     : 12345678 < 0 == 0

MSB = 1
  unsigned: abcdef01 >> 16 == 0000abcd
  int     : abcdef01 >> 16 == ffffabcd
  int     : abcdef01 < 0 == 1

0xabcdef01 == -1412567295
int の MSB(最上位ビット: most significant bit) は符号ビットで、
右シフトではそれが保存されます。MSB が 1 ならシフトしても 1。
unsigned int は、右シフトすると 0 が詰められます。

int である i の MSB が 1 の場合、それは負の値です。
i < 0 の演算結果は、i が 0 より小さければ 1、そうでなければ 0 になります。

hands
記事: 14
登録日時: 7年前

Re: if (i & 0x80000000) x = 1; else x = 0;

#11

投稿記事 by hands » 7年前

i < 0 にはそういう意味があったんですね。

最上位ビットを符号ビットとして使うことで、負の値を表現しているということでしょうか。

慣れの問題もあるでしょうが、シフトも難しく感じます。たくさんコードを書いて動かしながら感覚を掴んでいけたらと思っております。この度はたいへんお世話になりました。また何か疑問が生じたときは、フォーラムにあげさせて頂きますので、みてやってください。

かずま

Re: if (i & 0x80000000) x = 1; else x = 0;

#12

投稿記事 by かずま » 7年前

かずま さんが書きました:
7年前
i < 0 の演算結果は、i が 0 より小さければ 1、そうでなければ 0 になります。
すみません。
この説明は、C++ ではなく、C のものです。
JIS X3010:2003 プログラム言語 C

コード:

6.5.8 関係演算子
   :
<(小さい),>(大きい),<=(以下)及び>=(以上)の各演算子は,
指定された関係が真の場合は 1 を,偽の場合は 0 を返す。
その結果は,型 int をもつ。
C++ では、次のように true または false になります。
JIS X3014:2003 プログラム言語 C++

コード:

5.9 関係演算子  関係演算子は、左から右に結びついていく。
  例  a<b<c は、(a<b)<c であって、(a<b)&&(b<c) ではない。
    関係式:
        シフト式
        関係式 < シフト式
        関係式 > シフト式
        関係式 <= シフト式
        関係式 >= シフト式
演算対象の型は、算術型、列挙型、又は ポインタ型でなければならない。
演算子 < (小なり)、> (大なり)、<= (小なり 又は 等しい)及び
>= (大なり 又は 等しい) は、すべて false 又は true を生じる。
結果の型は、bool型とする。
true と false は、必要に応じて 1 または 0 に変換されます。

かずま

Re: if (i & 0x80000000) x = 1; else x = 0;

#13

投稿記事 by かずま » 7年前

JIS規格の PDF の URL は直接参照できないようです。

JIS検索 のJIS規格番号からJISを検索に、x3010 または x3014 を入れて検索してください。

hands
記事: 14
登録日時: 7年前

Re: if (i & 0x80000000) x = 1; else x = 0;

#14

投稿記事 by hands » 7年前

C

コード:

6.5.8 関係演算子
   :
<(小さい),>(大きい),<=(以下)及び>=(以上)の各演算子は,
指定された関係が真の場合は 1 を,偽の場合は 0 を返す。
その結果は,型 int をもつ。
C++

コード:

5.9 関係演算子  演算子
 < (小なり)、> (大なり)、<= (小なり 又は 等しい)及び
>= (大なり 又は 等しい) は、すべて false 又は true を生じる。
結果の型は、bool型とする。
C と C++では < 、 > 、<= 、>= の表すところが微妙に違ってくるんですね。成立した時期による表記のゆれですかね。新しい版ではこのあたりは統一されてるんでしょうか。いろいろと発見があって興味深いです。

今回初めて、規格を読むことができてわくわくしました。スマホでしたので、本当にちらっとしかみていないですが、一度は見てみたかったものなので「これか!」と。良いものを教えてくださいました。

時間があるときにゆっくり目を通したいです。ANSIとかISOとは何が違うんですかね。日本語かどうかという部分は違ってくるでしょうけど。単に日本語訳というわけではないですよね?ちょっとだけ気になりました。
かずま さんが書きました: JIS規格の PDF の URL は直接参照できないようです。
最初見たときは、ちゃんと見られましたよ。確かに今は見られなくなっていますね。
今後はJIS検索から見ます。貴重な情報ありがとうございました。いろいろ参考にさせていただきます。

hands
記事: 14
登録日時: 7年前

Re: if (i & 0x80000000) x = 1; else x = 0;

#15

投稿記事 by hands » 7年前

ANSIとかISOとは何が違うんですかね。日本語かどうかという部分は違ってくるでしょうけど。単に日本語訳というわけではないですよね?ちょっとだけ気になりました。
規格のド頭に書いてありましたね。

返信

“C言語何でも質問掲示板” へ戻る