これは何年も頭に浮かんだことですが、前に尋ねる時間はありませんでした。
多くの(疑似)乱数ジェネレーターが0.0から1.0までの乱数を生成します。数学的には、この範囲には無限の数がありますが、double
は浮動小数点数であるため、精度は有限です。
だから質問は:
double
の数はいくつですか?注:違いがある場合、特にJavaのdouble
の定義に興味があります。
Java double
sは IEEE-754 形式であるため、52ビットの小数になります。したがって、2つの隣接する2の累乗(1を含み、次の1を除く)の間には、2の52乗のdouble
sがあります(つまり、4503599627370496)。たとえば、これは、0.5が含まれていて1.0が除外されている間のdouble
sの数であり、その多くは、1.0が含まれているから2.0が除外されているなどのことも同様です。
0.0から1.0の間のdoubles
を数えることは、2のべき乗の間で行うよりも、2のべき乗がその範囲に含まれるため、非正規化数の厄介な問題に遭遇するため、困難です。指数の11ビットのうち10ビットが問題の範囲をカバーしているため、非正規化数(およびNaN
のいくつかの種類を含む)を含めると、間にdouble
sが1024倍あるはずです2の累乗-とにかく合計で2**62
以下。非正規化された&cを除くと、カウントは2**52
の1023倍になると思います。
「100から100.1」のような任意の範囲では、上限をdouble
として正確に表すことができないため、さらに困難です(2のべき乗の正確な倍数ではありません)。便利な近似として、2の累乗間の進行は線形であるため、その範囲は2の累乗(64と128)の間のスパンの0.1 / 64
thであると言えます。
(0.1 / 64) * 2**52
明確なdouble
s-これは7036874417766.4004
...になります。
表現が0x0000000000000000
と0x3ff0000000000000
の間にあるすべてのdouble
値は、間隔[0.0、1.0]にあります。これは(2 ^ 62-2 ^ 52)の異なる値です(エンドポイントをカウントするかどうかに応じて、プラスまたはマイナスのカップル)。
間隔[1.0、2.0]は、0x3ff0000000000000
と0x400000000000000
の間の表現に対応します。それは2 ^ 52の異なる値です。
間隔[100.0、101.0]は、0x4059000000000000
と0x4059400000000000
の間の表現に対応します。それは2 ^ 46の異なる値です。
10 ^ 100と10 ^ 100 + 1の間にはダブルはありません。これらの数値のどちらも倍精度で表現できず、それらの間にある倍精度はありません。最も近い2つの倍精度数は次のとおりです。
99999999999999982163600188718701095...
そして
10000000000000000159028911097599180...
[0.0、1.0]の範囲に約2 ^ 62の倍数があることをすでに説明している人もいます。
(本当に驚くべきことではありません:ほぼ2 ^ 64の異なる有限の倍精度数があります。そのうちの半分は正であり、約の半分 1.0未満です。)
しかし、乱数ジェネレータについて言及します。0.0と1.0の間の数を生成する乱数ジェネレータは、一般にこれらすべての数を生成できないことに注意してください。通常、n/2整数のn/2 ^ 53の形式の数値のみが生成されます(たとえば、Javaのドキュメント nextDouble を参照)。したがって、通常は約2 ^ 53(含まれるエンドポイントに応じて+/- 1)random()
出力の可能な値これは、[0.0、1.0]のほとんどのdoubleが生成されないことを意味します。
IBMの記事 Javaの新しい数学、パート2:浮動小数点数 は、これを解決する次のコードスニペットを提供しています(浮動小数点数ですが、倍精度浮動小数点数でも機能すると思います):
public class FloatCounter {
public static void main(String[] args) {
float x = 1.0F;
int numFloats = 0;
while (x <= 2.0) {
numFloats++;
System.out.println(x);
x = Math.nextUp(x);
}
System.out.println(numFloats);
}
}
彼らはそれについてこのコメントを持っています:
1.0と2.0の間には、正確に8,388,609の浮動小数点数があることがわかります。この範囲に存在する実数の数え切れないほどの無限大はほとんどありません。連続する数値は約0.0000001離れています。この距離は、精度が最も低い単位または最後の単位のULPと呼ばれます。
詳細は wikipediaの記事 を参照してください。
Java doubleはIEEE 754 binary64の数値です。
つまり、次のことを考慮する必要があります。
これは基本的に、標準に従って0と1の間の合計2 ^ 62-2 ^ 52 + 1の可能な二重表現があることを意味します。2^ 52 + 1は非正規化のケースを削除することに注意してください番号。
仮数が正で指数が負の場合、数値は正ですが1未満です:-)
他の数値の場合、IEEE 754表現ではEdge整数が正確に表現できない可能性があるため、また、数値を表すことができるように指数で使用される他のビットがあるため、少し難しいです。異なる値。