PSP xvi

PSPプログラミングを教えるブログ(本気で頑張る人アクセス大歓迎サイト)

PSP-X.gif

記事の間違いを報告  新アップローダー
記事修正情報 PSPプログラミング資料 自作ゲーム PSP用エミュレータ リンク

公認リンク
公式PSP専科 公式PSVita専科 はじめるPSPSDK PSP EXEC GAME M@STER PSP 猫山のYouTubeチャンネル

TAG
全記事にタグをつけています  http://nekoyama2gillien.blog36.fc2.com/?tag=タグ
PSP PSPプログラミング DXライブラリPortable OSLib ショートプログラム ハローワールド
PSP自作ゲーム PSP自作ソフト エミュレータ ゲームアーカイブス PSP動画
動画 初音ミク ミクミクダンス MMDドラマ ゲーム 魔法少女まどか☆マギカ 侵略!イカ娘
アイドルマスター

このブログについて

このブログでは、非公式のPSPソフト、いわゆる自作ソフト( PSP Homebrew )を作る事を目的とします。

著作権などの こまかい利用規約については、こちらを開いてお読み下さい

このブログについて知りたい方、初めて来訪された方はこちらを開いてお読みください
お問い合わせは 猫山猫宗(nekomune@gmail.com)までどうぞ


当ブログはリンクフリーです。ブログ名は、アルファベットで「PSP xvi」と書いて『ピーエスピー・エクシビ』とお読みください。
相互リンクを希望されるブログ運営者様は、ココで申請して下さい。


このブログで全記事から探し物の方は、ここをクリックして下さい。
http://nekoyama2gillien.blog36.fc2.com/?all

スポンサーサイト 

--/--/--
--. --:--

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

[edit]

CM: --
TB: --

page top

※※※※※  重要事項  ※※※※※

ある 神プログラマー様にご意見を伺ったところ、「シフト演算の部分に誤りがあるよん」と指摘されました。ついポカミスしそうなのでよーく考えてみました。で、修正しましたので気になる方は読み返して下さい。
ご指摘ありがとうなのです、神プログラマー様!!


神プログラマー様 に 頂いたURL ↓↓
http://ja.wikipedia.org/wiki/ビット演算
ココを見ると、ビット操作について詳しく書かれています。ナイスですっ!神プログラマー様 !!


私はまたもや ポカミス をしてしまったのです……………。
今度こそ、正しい事を書きます。


今回使った物 ↓↓
Minimalist PSPSDK v0.8.10 (PSPソフト開発キット / C++コンパイラ)

MAKEFILE のコンパイルオプション ↓↓
CFLAGS = -Wall -G0 -O2 -fomit-frame-pointer -mgp32 -mlong32


■■■■■ 右へシフト ■■■■■
main() // 論理シフト
{
unsigned int i; // 符号無し

i = 0xC0000000;
printf("16進数:%8X 10進数:%d \n",i,i);
i=i>>1; // 下位(右)方向へ1ビット分シフト
printf("16進数:%8X 10進数:%d \n",i,i);
}
実行結果
16進数:C0000000 10進数:-1073741824
16進数:60000000 10進数:1610612736

解説
論理演算なので、最上位ビットには 0 が入る(基本パターン)
算術演算ではありません!!
2進数:11000000 00000000 00000000 00000000 b = 16進数:0xC00000000
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   下位(右)方向へ1ビット分シフト
2進数:01100000 00000000 00000000 00000000 b = 16進数:0x600000000


main() // 算術シフト
{
int i; // 符号あり

i = 0xC0000000;
printf("16進数:%8X 10進数:%d \n",i,i);
i=i>>1; // 2分の1倍
printf("16進数:%8X 10進数:%d \n",i,i);
}
実行結果
16進数:C00000000 10進数:-1073741824
16進数:E00000000 10進数:-536870912

解説
算術演算なので、最上位ビットには符号ビットがコピーされて入る(ちょっと考えさせられるパターン)
10進数で見ると、ちゃんと2分の1倍になっています
2進数:11000000 00000000 00000000 00000000 b = 16進数:0xC00000000
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   2分の1倍
2進数:11100000 00000000 00000000 00000000 b = 16進数:0xE00000000


main() // 論理シフト
{
unsigned int i; // 符号無し

i = 0xC0000000;

printf("16進数:%8X 10進数:%d \n",i,i);
i=i>>2; // 下位(右)方向へ2ビット分シフト
printf("16進数:%8X 10進数:%d \n",i,i);
}
実行結果
16進数:C00000000 10進数:-1073741824
16進数:300000000 10進数:805306368

解説
論理演算なので、最上位ビットには 0 が入る(基本パターーン)
4分の1倍ではありません!(符号注意)
2進数:11000000 00000000 00000000 00000000 b = 16進数:0xC00000000
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   下位(右)方向へ2ビット分シフト
2進数:00110000 00000000 00000000 00000000 b = 16進数:0x300000000


main() // 算術シフト
{
int i; // 符号あり

i = 0xC0000000;
printf("16進数:%8X 10進数:%d \n",i,i);
i=i>>2; // 4分の1倍
printf("16進数:%8X 10進数:%d \n",i,i);
}
実行結果
16進数:C00000000 10進数:-1073741824
16進数:F00000000 10進数:-268435456

解説
算術演算なので、最上位ビットには符号ビットがコピーされて入る
ちゃんと4分の1倍になっています
2進数:11000000 00000000 00000000 00000000 b = 16進数:0xC00000000
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   4分の1倍
2進数:11110000 00000000 00000000 00000000 b = 16進数:0xF00000000


main() // 論理シフト
{
unsigned int i; // 符号無し

i = 0xFFFFFFFF;
printf("10進数:%d 16進数:%8X \n",i,i);
i=i>>31; // 下位(右)方向へ31ビットシフト
printf("10進数:%d 16進数:%8X \n",i,i);
}
実行結果
10進数:-1 16進数:FFFFFFFF
10進数:1  16進数:1

解説
31ビット分 シフトして、最上位ビット の 1 だけが残って移動し、値 = 1 になりました
2進数:11111111 11111111 11111111 11111111 b = 16進数:0xFFFFFFFF
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   下位(右)方向へ31ビットシフト
2進数:00000000 00000000 00000000 00000001 b = 16進数:0x00000001


main() // 論理シフト
{
unsigned int i; // 符号無し

i = 0xFA8759E5;
printf("16進数:%8X 10進数:%d \n",i,i);
i=i>>32; // 下位(右)方向へ32ビットシフト
printf("16進数:%8X 10進数:%d \n",i,i);
}
実行結果
16進数:FA8759E5 10進数:-91792923
16進数:FA8759E5 10進数:-91792923

解説
おや?変化がない??(値が変わってませんよ??)
2進数:11111010 10000111 01011001 11100101 b = 16進数:0xFA8759E5
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   下位(右)方向へ32ビット分 シフト??
2進数:11111010 10000111 01011001 11100101 b = 16進数:0xFA8759E5


main() // 0 ビット右シフトはどうなるか?(実験1)
{
unsigned int i; // 符号無し

i = 0x4E5D7A05; // 適当な数値(何でも可能)
printf("16進数:%8X 10進数:%d \n",i,i);
i=i>>0; // 下位(右)方向へ 0ビットシフト
printf("16進数:%8X 10進数:%d \n",i,i);
}
実行結果
16進数:4E5D7A05 10進数:1314748933
16進数:4E5D7A05 10進数:1314748933

解説
変化がありません(0 ビットシフトは、2の0乗倍なので1倍です。変化なしは当然)。
ビット移動してませんから、ね。
2進数:01001110 01011101 01111010 00000101 b = 16進数:0x4E5D7A05
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   下位(右)方向へ 0ビットシフト
2進数:01001110 01011101 01111010 00000101 b = 16進数:0x4E5D7A05


main() // 0 ビット左シフトはどうなるか?(実験2)
{
unsigned int i; // 符号無し

i = 0x12345678; // 適当な数値(何でも可能)
printf("16進数:%8X 10進数:%d \n",i,i);
i=i<<0; // 上位(左)方向へ 0ビットシフト
printf("16進数:%8X 10進数:%d \n",i,i);
}
実行結果
16進数:12345678 10進数:305419896
16進数:12345678 10進数:305419896

解説
変化がありません(0 ビットシフトは、2の0乗倍なので1倍です。変化なしは当然)。
下位(右)方向へ 0ビットシフトと同じ結果でした。
2進数:00010010 00110100 01010110 01111000 b = 16進数:0x12345678
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   上位(左)方向へ 0ビットシフト
2進数:00010010 00110100 01010110 01111000 b = 16進数:0x12345678


main() // 32ビットを越えるビットシフト(実験)
{
unsigned int i; // 符号無し

i = 0xFFFFFFFF;
printf("10進数:%d 16進数:%8X \n",i,i);
i=i>>49; // 下位(右)方向へ49ビットシフト
printf("10進数:%d 16進数:%8X \n",i,i);
}
実行結果
10進数:-1  16進数:FFFFFFFF
10進数:32767 16進数:00007FFF

解説
17ビット分だけ下位(右)方向へシフトしているッ!!!!
ひょっとして、49 を 32 で割った余り = 17 ビット分だけ論理シフトしているのかな?
2進数:11111111 11111111 11111111 11111111 b = 16進数:0xFFFFFFFF
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   下位(右)方向へ17ビット分だけシフト
2進数:00000000 00000000 01111111 11111111 b = 16進数:0x00007FFF


■■■■■ 左へシフト ■■■■■
main() // 算術演算 [正の数の場合]

int i = 123; // 正の値で 123

printf("10進数:%d 16進数:%8X \n",i,i);
i=i<<4; // 16倍
printf("10進数:%d 16進数:%8X \n",i,i);
}
実行結果
10進数: 123  16進数: 7B
10進数:1968  16進数: 7B0

解説
ちゃんと16倍されている
2進数:00000000 00000000 00000000 01111011 b = 16進数:0x00000007B
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   16倍
2進数:00000000 00000000 00000111 10110000 b = 16進数:0x0000007B0


main() // 算術演算 [負の数の場合]
{
int i = -10; // 負の値で -10

printf("10進数:%d 16進数:%8X \n",i,i);
i=i<<2; // 4倍
printf("10進数:%d 16進数:%8X \n",i,i);
}
実行結果
10進数:-10 16進数:FFFFFFF6
10進数:-40 16進数:FFFFFFD8

解説
ちゃんと4倍になっている
2進数:11111111 11111111 11111111 11110110 b = 16進数:0xFFFFFFF6
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   4倍
2進数:11111111 11111111 11111111 11011000 b = 16進数:0xFFFFFFD8


main() // 算術演算 [負の数の場合] 桁あふれ例1
{
int i = 0x90000000;

printf("10進数:%d 16進数:%8X \n",i,i);
i=i<<1; // 2倍
printf("10進数:%d 16進数:%8X \n",i,i);
}
実行結果
10進数:-1879048192 16進数:900000000
10進数:536870912  16進数:200000000

解説
これは、演算結果が 桁あふれ になった為。エラーです。
2進数:10010000 00000000 00000000 00000000 b = 16進数:0x90000000
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   2倍
2進数:00100000 00000000 00000000 00000000 b = 16進数:0x20000000


main() // 算術演算 [負の数の場合] 桁あふれ例2
{
int i = 0x80000000;

printf("16進数:%8X 10進数:%d \n",i,i);
i=i<<1; // 2倍
printf("16進数:%8X 10進数:%d \n",i,i);
}
実行結果
16進数:800000000 10進数:-2147483648
16進数:0  10進数:0

解説
これも、演算結果が 桁あふれ になった為。エラーです。
2進数:10000000 00000000 00000000 00000000 b = 16進数:0x80000000
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   2倍
2進数:00000000 00000000 00000000 00000000 b = 16進数:0x00000000


main() // 32ビットを越えるビットシフト(実験1)
{
unsigned int i = 0xFFFFFFFF;

printf("16進数:%8X 10進数:%d \n",i,i);
i=i<<50; // 上位(左)方向へ 50ビット分シフト
printf("16進数:%8X 10進数:%d \n",i,i);
}
実行結果
16進数:FFFFFFFF 10進数:-1
16進数:FFFC0000 10進数:-262144

解説
これも、50 を 32 で割った余り = 18 ビット分だけ シフトしています
2進数:11111111 11111111 11111111 11111111 b = 16進数:0xFFFFFFFF
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   上位(左)方向へ18ビット分だけシフト
2進数:11111111 11111100 00000000 00000000 b = 16進数:0xFFFC0000


main() // 負の数ビット分シフト(実験1)
{
unsigned int i = 0xFF39E421;

printf("16進数:%8X 10進数:%d \n",i,i);
i=i>>(-3); // 下位(右)方向へ、-3ビット分シフト
printf("16進数:%8X 10進数:%d \n",i,i);
}
実行結果
16進数:FF39E421 10進数:-12983263
16進数:F9CF2108 10進数:-103866104

解説
負の数ビット分のシフトは、逆方向への正の数ビット分のシフトになる様です
2進数:11111111 00111001 11100100 00100001 b = 16進数:0xFF39E421
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   上位(左)方向へ 3ビット分シフト
2進数:11111001 11001111 00100001 00001000 b = 16進数:0xF9CF2108


main() // 負の数ビット分シフト(実験2)
{
unsigned int i = 0xFA47D8B1;

printf("16進数:%8X 10進数:%d \n",i,i);
i=i<<(-8); // 上位(左)方向へ、-8ビット分シフト
printf("16進数:%8X 10進数:%d \n",i,i);
}
実行結果
16進数:FA47D8B1 10進数:-95954767
16進数:0FA47D8B 10進数:16402392

解説
負の数ビット分のシフトは、逆方向への正の数ビット分のシフトになる様です
2進数:11111010 01000111 11011000 10110001 b = 16進数:0xFA47D8B1
↓↓↓↓ ↓↓↓↓ ↓↓↓↓ ↓↓↓↓   下位(左)方向へ 8ビット分シフト
2進数:00001111 10100100 01111101 10001011 b = 16進数:0x0FA47D8B


32ビット以上のシフト演算についての参考文献(神プログラマー様 から頂いたURL)
http://www.cpptalk.net/shifting-bits-shift-32-bits-on-32-bit-int-vt35538.html
抜粋すると、こう書いてあります。
> uint32_t x = 5;
> uint32_t y = x << 32;
> uint32_t z = x << 32;
> 
> In the above example y and z are both still 5. Why is this?

  翻訳 : シフト演算の結果、値が変化しないのは何故?

32ビット長の値(変数)を 32ビット左にシフトして 0 にしたかったんでしょうね、気持ちは解かります。
でも、そういう規則(?)はコンパイラーの設計側での問題なので、プログラマーにはコーディングで頑張るしかないですよ。
こまめにデバッグして調べるしか無いようですね。
コンパイラの変な動作を発見したら、原始的な方法で対処するとかした方が良いかもです。
それと、変テコなプログラムは書かないことが鉄則です!



念の為、もう一回 書いておきます 。
今回使った物 ↓↓
Minimalist PSPSDK v0.8.10 (Win32アプリ / PSPソフト開発キット)

MAKEFILE のコンパイルオプション ↓↓
CFLAGS = -Wall -G0 -O2 -fomit-frame-pointer -mgp32 -mlong32

ちょっと一言

今回、こういう結果になりましたが、他のC++コンパイラでも必ずしもこういう結果になるとは限りません。
更に、同じコンパイラでも、コンパイルオプションが違うと 微妙に異なる実行ファイル(EBOOT.PBPや****.prx)が出来上がるので要注意です。



◆◆◆◆◆  まとめ  ◆◆◆◆◆

・キャスト(型宣言)は正しく記述しましょう。

・シフト演算には、論理演算と算術演算があります。
・論理演算は、符号を無視します。nビットシフト。
・算術演算は、符号を有視します。上位(左)方向へは2のn乗倍。下位(左)方向へは2のn乗分の1倍。
・論理演算か、算術演算かは、指定した 変数のキャストを見れば解かります。

・PSPSDKでの ビットシフト演算の処理は、どうやらシフトするビット数を 32 で割った余りの値だけシフトするっぽいです(多分)。
・32ビット分シフトの場合は、0ビット分シフトという事で変化がありません(PSPSDKで検証)。

・負の数ビット(-n)分のシフトは、逆方向への正の数ビット(n) 分のシフトになる様です
 (例1) num>>(-5) は num<<5 の様に処理される
 (例2) num<<(-7) は num>>7 の様に処理される

値は 32ビット長なので、桁あふれする場合があります。警告!!
 が、しかし、桁あふれしても、PSPはエラーだと気付かないのです。
 桁あふれしたらエラー処理をする、というプログラムにての値チェックをするしか無いようです。

 (シフト演算に桁あふれエラー処理を記述するって、聞いた事ありませんが………)


■■■ コンパイル中のメッセージは良く見て下さい ■■■
32ビットを越えるシフト演算を記述したプログラムソースをコンパイルすると、
warning: left shift count >= width of type
warning: right shift count >= width of type
↑↑のような ウォーニング(警告) が表示されます。

負の数ビット分のシフト演算を記述したプログラムソースをコンパイルすると、
warning: left shift count is negative
↑↑のような ウォーニング(警告)が表示されます。

WARNING は、警告でありまして、エラーではありません。その証拠に、コンパイルが継続します。
神プログラマー様によると、"WARNING"は正しくは ウォーニング と発音するのだそうです。

うーーん、勉強になりました。サンキューですぞ。ウホ。

関連記事
スポンサーサイト

[edit]

CM: 2
TB: 0

page top

この記事に対するコメント

ほほう

符号無し変数にシフトは危険なんですか
気をつけないとずっと嵌りそう。

シフトで32ケタもずらす機会なんて今後あるかわからないですけど、桁数指定を変数で指定してるともしかしたらこういった問題を起こすかもしれないですね。

勉強になりました。

pppm #rkzq7EIc | URL | 2009/07/01 12:44 * edit *

う~ん。

符号無しの値なら、シフト演算は単純に考えられますが、
符号在りの値は、算術シフト演算で考えるのです。
符号を考慮してやるベシっ!!

で、シフト演算を変数でやると…。

int i,j;

i=3;
for (j=-3;j<3;j++)
{
   i=i>>j;
}

こうなります。負の値の時は(j<0)、

i=i<<(-j); (-j は 正の値)

となりますね。シフトの方向が逆になるよん。



猫山猫宗 #HfMzn2gY | URL | 2009/07/01 16:22 * edit *

page top

コメントの投稿

Secret

page top

トラックバック

トラックバックURL
→http://nekoyama2gillien.blog36.fc2.com/tb.php/97-7f7a6e3d
この記事にトラックバックする(FC2ブログユーザー)

page top


h o m e |


 

2017-07