• 締切済み

g++のrand

VisualStudioで正常に動いていた乱数を用いたプログラムをLinuxのg++でコンパイルするとコンパイルはできるが正しい挙動をしないという問題に出くわしました。 調べていったところ、 float r = (float)rand() / (RAND_MAX+1); という処理のRAND_MAX+1がまずかったようです。 プリントして確認したら print(RAND_MAX) … 2147483647 print(RAND_MAX+1) … -2147483648 となっています。 オーバーフローしたためであろうということは検討がつくのですが、 理屈がはっきりわからず、すっきりしない感じです。 なぜ、RAND_MAXに+1をすると-2147483648になるのか(intが符号付だから??)、なぜwindows(Visual Studio)だと問題なかったのに、Linux(g++)だとダメなのか、 非常に初歩な質問かもしれませんが、どなたかご解説をお願いします。

みんなの回答

回答No.7

>なぜwindows(Visual Studio)だと問題なかったのに、Linux(g++)だとダメなのか 理由は「Visual StudioではRAND_MAXはintの最大値よりも小さく、g++ではRAND_MAXはintの最大値と等しかった」からです。 float r = (float)rand() / (RAND_MAX+1); の式の「(RAND_MAX+1)」の部分は「intで計算」されます。 (RAND_MAX+1)をintで計算した時「RAND_MAXがintの最大値よりも小さい」ならば「RAND_MAX+1」は「intの最大値を超えないのでオーバーフローしない」です。 (RAND_MAX+1)をintで計算した時「RAND_MAXがintの最大値と等しい」ならば「RAND_MAX+1」は「intの最大値を超えてオーバーフローする」のです。 「処理系により、RAND_MAXはintの最大値よりも小さかったり、intの最大値と等しかったりする」のです。つまり「intで1を足しても大丈夫な処理系と、大丈夫じゃない処理系がある」のです。 それを踏まえず、安易に「(RAND_MAX+1)」と書くのは「RAND_MAXがintの最大値よりも小さく、intで1を足してもintの最大値を超えない(オーバーフローしない)だろう事を期待している」ので、この記述は「処理系依存」であり「移植時にバグを発生させる危険性」があります。 こういう「処理系に依存する定義」は、色々な所に潜んでいるので「intで1を足す前に、intのまま足しても安全かどうか確かめる」ようにしましょう。 もちろん「このコンパイラで大丈夫だったから」ってのは通用しません。なぜなら「このコンパイラ『だけ』で大丈夫だっただけで、他では大丈夫じゃない」かも知れないですから。

mats_may
質問者

お礼

>もちろん「このコンパイラで大丈夫だったから」ってのは通用しません。なぜなら「このコンパイラ『だけ』で大丈夫だっただけで、他では大丈夫じゃない」かも知れないですから。 なるほど、勉強になります。 自分はLinuxの経験がほとんどなく、今回が始めての移植対応だったのでコンパイラの処理系の違いについてほとんど無知でした。 winで動いている分、「なんで」と非常に混乱しましたが、 わかると納得な理由です。 他にも処理系依存の定義が色々あるということで、 場数を踏みながら、移植性を考慮したログラムも書けるように 勉強していこうと思います。 大変参考になりました。 ありがとうございました。

  • php504
  • ベストアンサー率42% (926/2160)
回答No.6

0x7fffffff + 1 = 0x80000000 = -2147483648 という理屈です

mats_may
質問者

補足

補数というものですか? これがintの仕様なのでしょうか? 自分でも調べていますが、この理屈がいまいちわかりません…

  • php504
  • ベストアンサー率42% (926/2160)
回答No.5

cygwinでは #if __INT_MAX__ == 32767 #define __RAND_MAX 32767 #else #define __RAND_MAX 0x7fffffff #endif #define RAND_MAX __RAND_MAX VisualC++では #define RAND_MAX 0x7fff でした

mats_may
質問者

補足

なるほど、このように定義されているのですね。 ちなみにこれはどのように確認されたのですか?

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.4

規格上、RAND_MAXは32767以上としか決まっていません。 つまり、これ以上大きい値であればいくつでもかまわないのです。 移植性が必要な場合には、その辺のことを配慮しなければなりません。

mats_may
質問者

お礼

>移植性が必要な場合には、その辺のことを配慮しなければなりません。 正直移植ということは今回が初めてで今まではほとんどwinベースの開発しか行ったことがありません。コンパイラによる違いを理解するという意味でも今回のバグは非常により勉強になりました。 ご回答ありがとうございました。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

普通は rand() / (RAND_MAX+1.0) などとします. ちなみに g++ で RAND_MAX+1 が -2147483648 になったのは純粋にオーバーフローしたから, と言っていいとは思います. 厳密にはいろいろありますが. あと, RAND_MAX そのものは 32767 以上であることしか規定されていません. Visual Studio は 32767 だか 65535 だか, その辺だったはずです.

mats_may
質問者

お礼

>RAND_MAX そのものは 32767 以上であることしか規定されていません. なるほど、だからコンパイラによる違いが顕著なんですか… 移植性を持たせる場合は乱数は自分で作るorオープンソースの者を使った方がよいかもしれませんね。 ありがとうございました。

  • rinkun
  • ベストアンサー率44% (706/1571)
回答No.2

2147483647 が 32ビットのint型で表せる最大の数だからです。 このため+1するとオーバーフローして最小の数になっています。 Visual StudioでのRAND_MAXは(確認していませんが)もっと小さい数なのではないかと思います。

mats_may
質問者

お礼

やはりオーバーフローのようですね。 ありがとうございました。

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.1

RAND_MAXをfloatにキャストするか +1をfloat型の +1.0fなどにする float r = ( float )rand() / ( (float)RAND_MAX + 1 ); または float r = ( float )rand() / ( RAND_MAX + 1.0f ); といった具合にする

mats_may
質問者

お礼

なるほど、 float r = ( float )rand() / ( RAND_MAX + 1.0f ); のように書くと RAND_MAX + 1.0f はfloatに合わされるのですね。 参考になりました。