2016-08-04 4 views
6

意味((フロート)のrand()/(フロート)((1 << 31) - 1))私はラインと.hファイルが含まれてCプログラムを理解しようとしている

#define random     ((float) rand()/(float)((1 << 31) - 1)) 

Cプログラムには<math.h>も含まれています。

私は、これは単に区間[0,1]の一様分布から乱数を生成すると考えています。これは正しいです?

+0

未定義の挙動であろう。何かが起きることがあります。 – Olaf

+1

sizeof(int)== 4の場合、[Is 1 << 31はCで十分に定義されています。](https://stackoverflow.com/questions/45268467/is-131-well-defined-in-c-when- sizeofint-4) –

答えて

10

表面上はい。しかし、それは2つの主要な方法で間違っています。

  1. 代わりにRAND_MAXを使用してください。それがそこにあるのです。それは1 << 31 - 1よりずっと小さいかもしれません。

  2. 1 << 31が格段によくある32ビットint以下、とあなたのプラットフォーム上で未定義の動作を与えるだろう。それをしないでください! (しばしばそうであるように)あなたは値1を、回復したくない場合は、分母にRAND_MAX + 1.0を使用することを

注意。 1.0浮動小数点の評価を強制する:RAND_MAX + 1と書いた場合、整数型のオーバーフローの危険があります。

+0

16ビット以上33ビット未満のプラットフォームでもUBを呼び出します。 'RAND_MAX + 1'はすべてのプラットフォームでUBを呼び出す可能性が非常に高いです。 – Olaf

+0

あなたが気にしないなら、私は答えを編集しました。両方とも非常に良い点です。ありがとうございました。 –

+0

'RAND_MAX + 1.0'は' double'型ではありませんか? –

5

rand関数は、0とRAND_MAXの間の値を返します。このプログラムは、RAND_MAXが2^31 - 1であり、その数値で結果を除算していると仮定しています。

上記の仮定が真であれば、このマクロは[0,1]からの数値を返します。あまり均一なランダム分布ではなく、むしろ「疑似ランダム」値です。

少なくともそれはと思われます。とします。この式(1 << 31)は、1の型がintで、31が左シフトするとintの範囲外になるため、未定義の動作が発生します(intは32ビット以下です)。実際には、2の補数表現が使用されている場合、コンパイラによってはこのシフトが許され、次に-1がそれを範囲に戻しますが、依存することはできません。

(1U << 31)を使用すると、定義されていない動作を避けることができます。この場合、定数1unsigned intとなり、範囲内にシフトします。より良い選択肢は、シフトを忘れて、控除して0x7fffffffを使用することです。

しかし、最大の移植のために、次のように定義する必要があります。

#define random ((float)rand()/RAND_MAX) 

しかし、問題はまだあります。 floatは、通常、23ビットの仮数を有する。 randが32ビットの値を返す場合は、適切な数値の分布が得られません。52ビットの仮数を有する活用使用double

#define random ((double)rand()/RAND_MAX) 
+0

UBはありませんか? – Olaf

+0

@Olaf Right。その詳細を追加しました。 – dbush

+0

そのようなシフトには問題があります。 32ビット未満の実装では、依然としてUBを呼び出します(シフトカウント>ビット幅)。 'RAND_MAX'アプローチがより良いです。しかし、 'float'を使用すると精度が失われ、通常は23ビット(IEEEの浮動小数点型の仮数)以下に限定されます。 – Olaf

1

私の推測では、これは単に区間[0,1]上の一様分布から乱数を生成することです。これは正しいです?

コードは決して1.0fを返すことはありません。

#define random ((float) rand()/(float)((1 << 31) - 1))には多くの問題があります。弱いコードです。


精度の失われた:標準float精度の約24ビットを有しています。 rand()の結果を変換すると、24ビットを超えるとfloatとなり、丸めによって元の値と異なる場合があります。これにより、乱数生成のの均一性が弱められ/破壊されます。異なるrand()の結果には同じ回答が出てくるでしょう。参照@Olaf

OPが明らかに集合から一様乱数たいとしての修正は問題がある可能性が高い精度所与不可能である[0、1/2,147,483,648、2/2,147,483,648を、.../2,147,483,648 2,147,483,647]制限はfloatです。


最悪は、intが少なくとも33ビット長である場合を除き(1 << 31)未定義の動作 UBであることです。記号位置に1を移動するのはUBです。 C11dr§6.5.74.

UBを避けるには、((1ul << 31) - 1)を使用してください。

しかし、マジックナンバー((1ul << 31) - 1)を使用すると、RAND_MAXの分数を基準にするほど頑丈ではありません。

さらに(float) ((1ul << 31) - 1)は、値2147483648.0fを形成し、入手不可能な2147483647.0fを形成しないので、上記のように精度が低下する可能性があります。オペレーターのコード1.0fを生成することはありません。


OPには[0..1]の結果が必要で、[0..1]は必要ないと思われます。どちらも以下の通りです。

// generate a `double` in the range [0 ... 1) 
#define random0to_almost1 (rand()/(RAND_MAX + 1.0)) 
// or 
// generate a `double` in the range [0 ... 1] 
#define random0to1 (rand()/(RAND_MAX + 0.0)) 

これはOPの元のコードのように苦しんでいることを注意double(標準53ビット)の精度がRAND_MAXニーズを超えているべきです。

対処するには、RAND_MAX + 1.0が正確に行われることを確実にすることです。 では、非常に一般的なが指定されていますが、Cは指定されていません。RAND_MAXは2のべき乗です_minus_1です。したがってRAND_MAX/2 + 1intであり、正確なの2の累乗です。intからdoubleへの変換は確かに正確なです。

#define random0to_almost1 (rand()/(2.0*(RAND_MAX/2 + 1))) 

float

溶液)はせいぜい32ビットを有するないint` `とすべてのプラットフォームで

// This value is platform dependent, but very common 
// Do not a a highly portable generation method yet. 
#define FLT_POWER2_INTEGER_LIMIT (1ul << 24) 


#define random0to_almost1 (\ 
    (rand() % FLT_POWER2_INTEGER_LIMIT)/\ 
    (RAND_MAX % FLT_POWER2_INTEGER_LIMIT + 1.0f) \ 
    ) 
+1

'FLT_RADIX'が2の累乗でない場合、' int'値の 'double'への変換は正確ではないかもしれません。 –

+0

@Ian Abbottはい - 合意しました。 Cは '2,3,4,5 ...'の 'FLT_RADIX'を許します。私は' 2,10,16'以外の 'FLT_RADIX'にはまだ来ていません。もちろん将来は異なるかもしれません。確かに '2,4,8,16、.. 'の値は上記の答えには問題ではありません。ベース10では、上記の方法を使用しても利点はありますが、_exactness_に達しない可能性があります。今日使用されているベース以外の2台のシステムについて知っていれば、それらのヒアリングに最も興味があります。 – chux

関連する問題

 関連する問題