<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/class.random-randomizer.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'ja',
  ),
  'this' => 
  array (
    0 => 'random-randomizer.getfloat.php',
    1 => 'Random\\Randomizer::getFloat',
    2 => '等確率に選んだ float の値を取得する',
  ),
  'up' => 
  array (
    0 => 'class.random-randomizer.php',
    1 => 'Random\\Randomizer',
  ),
  'prev' => 
  array (
    0 => 'random-randomizer.getbytesfromstring.php',
    1 => 'Random\\Randomizer::getBytesFromString',
  ),
  'next' => 
  array (
    0 => 'random-randomizer.getint.php',
    1 => 'Random\\Randomizer::getInt',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ja',
    'path' => 'reference/random/random/randomizer/getfloat.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="random-randomizer.getfloat" class="refentry">
 <div class="refnamediv">
  <h1 class="refname">Random\Randomizer::getFloat</h1>
  <p class="verinfo">(PHP 8 &gt;= 8.3.0)</p><p class="refpurpose"><span class="refname">Random\Randomizer::getFloat</span> &mdash; <span class="dc-title">等確率に選んだ float の値を取得する</span></p>

 </div>

 <div class="refsect1 description" id="refsect1-random-randomizer.getfloat-description">
  <h3 class="title">説明</h3>
  <div class="methodsynopsis dc-description">
   <span class="modifier">public</span> <span class="methodname"><strong>Random\Randomizer::getFloat</strong></span>(<span class="methodparam"><span class="type"><a href="language.types.float.php" class="type float">float</a></span> <code class="parameter">$min</code></span>, <span class="methodparam"><span class="type"><a href="language.types.float.php" class="type float">float</a></span> <code class="parameter">$max</code></span>, <span class="methodparam"><span class="type"><a href="enum.random-intervalboundary.php" class="type Random\IntervalBoundary">Random\IntervalBoundary</a></span> <code class="parameter">$boundary</code><span class="initializer"> = <strong><code>Random\IntervalBoundary::ClosedOpen</code></strong></span></span>): <span class="type"><a href="language.types.float.php" class="type float">float</a></span></div>

  <p class="para rdfs-comment">
   指定した区間に一様に分散した浮動小数点数の中から、等確率に選んだ値を返します。
  </p>
  <p class="para">
   精度が限られているため、
   全ての実数が正確に浮動小数点数として表現できるとは限りません。

   数値が正確に表現できない場合、値は表現できるもっとも近い値に丸められます。

   さらに浮動小数点数の値は、数直線全体に均等に分布しているわけではありません。

   浮動小数点数の値は 2進数の指数を使うので、隣り合うふたつの値の距離は、
   それぞれの2のべき乗ごとに2倍になります。

   つまり: <code class="literal">1.0</code> と <code class="literal">2.0</code>
   の間で表現できる 浮動小数点数 の数は、<code class="literal">2.0</code> と <code class="literal">4.0</code>
   の間で表現できる数、<code class="literal">4.0</code> と <code class="literal">8.0</code>
   の間で表現できる数、<code class="literal">8.0</code> と <code class="literal">16.0</code>
   の間で表現できる数と同じ... といった具合です。
  </p>
  <p class="para">
   こうした理由から、例えば2つの整数を割るなどして、
   要求された区間内の任意の数をランダムサンプリングすると、
   分布が偏る可能性があります。

   必要な丸めを行うと、
   浮動小数点数の値によっては別の値より多くの頻度で返されることがあります。
   浮動小数点数の値の密度が変わる2のべき乗周辺の値については、特にそれが当てはまります。
  </p>
  <p class="para">
   <span class="methodname"><strong>Random\Randomizer::getFloat()</strong></span> は、
   指定した区間内で正確に表現でき、
   かつ一様に分散した浮動小数点数の可能な限り大きな集合から、
   等確率に値を返すアルゴリズムを実装します。

   選択可能な浮動小数点数の間の距離 (ステップサイズ) は、
   密度が最も小さい浮動小数点数の間の距離、つまり、
   絶対値がより大きな区間境界の浮動小数点数の距離に一致します。

   これは、区間が2のべき乗を1つ以上横切る場合、
   与えられた区間内で表現可能な浮動小数点数の値がすべて返されるとは限らないということです。

   ステップは、絶対値の大きい区間境界から始まります。
   これは、ステップを正確に表現可能な浮動小数点数と一致させるためです。
  </p>
  <p class="para">
   閉区間の境界は、常に選択可能な浮動小数点数の集合に含まれます。

   よって、区間のサイズがステップサイズの正確な倍数でなく、
   かつ絶対値が小さな方の境界が閉じている場合、
   その境界とそれにもっとも近い選択可能な浮動小数点数との間の距離は、
   ステップサイズよりも小さくなります。
  </p>
  <div class="caution"><strong class="caution">警告</strong>
   <p class="para">
    このメソッドが返す float の値を後処理すると、
    値の一様な分布を壊す可能性があります。なぜなら、
    数学演算に含まれる中間的な浮動小数点数の値は暗黙のうちに丸められているからです。

    指定された区間は、できるだけ望ましい区間と一致させるべきですし、
    丸め処理はこのメソッドが選択した値をユーザーに表示させる直前に明示的に実行すべきです。
   </p>
  </div>
  <div class="refsect2 unknown-31" id="refsect2-random-randomizer.getfloat-unknown-31">
   <h4 class="title">サンプルの浮動小数点表現を使った、アルゴリズムの説明</h4>
   <p class="para">
    アルゴリズムがどのように動作するかの例を示すために、
    3ビットの仮数を使用する浮動小数点表現を考えてみましょう。

    この表現は、連続する2のべき乗の間で、8つの異なる浮動小数点数を表現することができます。

    つまり、<code class="literal">1.0</code> と <code class="literal">2.0</code>
    の間では <code class="literal">0.125</code> のすべてのステップサイズを正確に表現でき、
    <code class="literal">2.0</code> と <code class="literal">4.0</code>
    の間では <code class="literal">0.25</code> のすべてのステップサイズを正確に表現できるということです。

    実際には、PHP の float は 52 ビットの仮数を使用しており、
    それぞれの 2 のべき乗の間で 2<sup class="superscript">52</sup>
    個の異なる値を表現することができます。

    つまり、以下の数が、<code class="literal">1.0</code> と <code class="literal">4.0</code>
    の範囲で正確に表現できるということです。

    <ul class="simplelist">
     <li><code class="literal">1.0</code></li>
     <li><code class="literal">1.125</code></li>
     <li><code class="literal">1.25</code></li>
     <li><code class="literal">1.375</code></li>
     <li><code class="literal">1.5</code></li>
     <li><code class="literal">1.625</code></li>
     <li><code class="literal">1.75</code></li>
     <li><code class="literal">1.875</code></li>
     <li><code class="literal">2.0</code></li>
     <li><code class="literal">2.25</code></li>
     <li><code class="literal">2.5</code></li>
     <li><code class="literal">2.75</code></li>
     <li><code class="literal">3.0</code></li>
     <li><code class="literal">3.25</code></li>
     <li><code class="literal">3.5</code></li>
     <li><code class="literal">3.75</code></li>
     <li><code class="literal">4.0</code></li>
    </ul>
   </p>
   <p class="para">
    さてここで、<code class="code">$randomizer-&gt;getFloat(1.625, 2.5, IntervalBoundary::ClosedOpen)</code> がコールされた場合を考えてみましょう。
    つまり、<code class="literal">1.625</code> から始まり、<code class="literal">2.5</code>
    までのランダムな値 (ただし <code class="literal">2.5</code> そのものは含みません)
    を要求するということです。

    このメソッドのアルゴリズムは、
    まず絶対値の大きい境界(<code class="literal">2.5</code>)でのステップサイズを決定します。
    今回の境界の場合、ステップサイズは <code class="literal">0.25</code> です。
   </p>
   <p class="para">
    ここで、要求された区間のサイズは <code class="literal">0.875</code> であり、
    <code class="literal">0.25</code> の正確な倍数ではないことに注意してください。

    もしアルゴリズムが下界 <code class="literal">1.625</code>からステップを開始すると、
    正確に表現できない <code class="literal">2.125</code> が現れた時点で、暗黙の丸めが発生します。

    そのため、アルゴリズムは上界 <code class="literal">2.5</code> からステップを開始するのです。

    選択可能な値は、以下のとおりです:

    <ul class="simplelist">
     <li><code class="literal">2.25</code></li>
     <li><code class="literal">2.0</code></li>
     <li><code class="literal">1.75</code></li>
     <li><code class="literal">1.625</code></li>
    </ul>

    <code class="literal">2.5</code> は選択可能な値に含まれません。
    なぜなら、要求された区間の上界が開いているからです。

    <code class="literal">1.625</code> は選択可能な値に含まれます。
    この値は最も近い値 <code class="literal">1.75</code> との距離が <code class="literal">0.125</code>
    であり、以前決定したステップサイズ <code class="literal">0.25</code>より小さいのですが、
    それにも関わらず、含まれます。

    その理由は、要求された区間は下側の境界(<code class="literal">1.625</code>)で閉じており、
    閉じた境界は常に含まれるからです。
   </p>
   <p class="para">
    最後に、アルゴリズムは4つの選択可能な値から等確率で値をランダムに選び、それを返します。
   </p>
   <div class="refsect3 unknown-34" id="random-randomizer.getfloat.affine-transformation">
    <h5 class="title">2つの整数値を割るやり方ではなぜダメなのか</h5>
    <p class="para">
     上の例では、2のべき乗で区切られたそれぞれの区間に、
     表現可能な浮動小数点数が8個あります。

     ランダムな浮動小数点数を生成するのに、
     なぜ2つの整数を割る方法がうまくいかないのかを示すために、
     <code class="literal">0.0</code> から <code class="literal">1.0</code>
     を含まない右開きの区間に、16個の一様に分散した浮動小数点数があるとしましょう。

     そのうち半分は、<code class="literal">0.5</code> から <code class="literal">1.0</code>
     までの間で8つの値として正確に表現できますが、
     残りの半分は <code class="literal">0.0</code> から <code class="literal">1.0</code>
     までの間でステップサイズが <code class="literal">0.0625</code> の値です。
     これらの値は、<code class="literal">0</code> から <code class="literal">15</code>
     の間のランダムな整数を <code class="literal">16</code>
     で割ることによって、以下のうちのひとつを簡単に生成することができます:

     <ul class="simplelist">
      <li><code class="literal">0.0</code></li>
      <li><code class="literal">0.0625</code></li>
      <li><code class="literal">0.125</code></li>
      <li><code class="literal">0.1875</code></li>
      <li><code class="literal">0.25</code></li>
      <li><code class="literal">0.3125</code></li>
      <li><code class="literal">0.375</code></li>
      <li><code class="literal">0.4375</code></li>
      <li><code class="literal">0.5</code></li>
      <li><code class="literal">0.5625</code></li>
      <li><code class="literal">0.625</code></li>
      <li><code class="literal">0.6875</code></li>
      <li><code class="literal">0.75</code></li>
      <li><code class="literal">0.8125</code></li>
      <li><code class="literal">0.875</code></li>
      <li><code class="literal">0.9375</code></li>
     </ul>
    </p>
    <p class="para">

     このランダムな浮動小数点の値は、
     <code class="literal">1.625</code> から <code class="literal">2.75</code> まで
     (<code class="literal">2.75</code> を含まない) の右開きの区間に、
     区間の大きさ(<code class="literal">0.875</code>) を掛け合わせ、
     最小の <code class="literal">1.625</code> を足すことで拡大変換できます。
     この、いわゆるアフィン変換の結果は、以下のような値になります:

     <ul class="simplelist">
      <li><code class="literal">1.625</code> は、<code class="literal">1.625</code> に丸められます</li>
      <li><code class="literal">1.679</code> は、<code class="literal">1.625</code> に丸められます</li>
      <li><code class="literal">1.734</code> は、<code class="literal">1.75</code> に丸められます</li>
      <li><code class="literal">1.789</code> は、<code class="literal">1.75</code> に丸められます</li>
      <li><code class="literal">1.843</code> は、<code class="literal">1.875</code> に丸められます</li>
      <li><code class="literal">1.898</code> は、<code class="literal">1.875</code> に丸められます</li>
      <li><code class="literal">1.953</code> は、<code class="literal">2.0</code> に丸められます</li>
      <li><code class="literal">2.007</code> は、<code class="literal">2.0</code> に丸められます</li>
      <li><code class="literal">2.062</code> は、<code class="literal">2.0</code> に丸められます</li>
      <li><code class="literal">2.117</code> は、<code class="literal">2.0</code> に丸められます</li>
      <li><code class="literal">2.171</code> は、<code class="literal">2.25</code> に丸められます</li>
      <li><code class="literal">2.226</code> は、<code class="literal">2.25</code> に丸められます</li>
      <li><code class="literal">2.281</code> は、<code class="literal">2.25</code> に丸められます</li>
      <li><code class="literal">2.335</code> は、<code class="literal">2.25</code> に丸められます</li>
      <li><code class="literal">2.390</code> は、<code class="literal">2.5</code> に丸められます</li>
      <li><code class="literal">2.445</code> は、<code class="literal">2.5</code> に丸められます</li>
     </ul>

     上界の値 <code class="literal">2.5</code> が、
     開区間であるため除外されるべきなのに、返されてしまう点に注意してください。

     また、<code class="literal">2.0</code> と <code class="literal">2.25</code> が、
     他の値に比べて2倍の確率で返されることにも注意しましょう。
    </p>
   </div>

  </div>

 </div>


 <div class="refsect1 parameters" id="refsect1-random-randomizer.getfloat-parameters">
  <h3 class="title">パラメータ</h3>
  <p class="para">
   <dl>
    
     <dt><code class="parameter">min</code></dt>
     <dd>
      <p class="para">
       区間の下限
      </p>
     </dd>
    
    
     <dt><code class="parameter">max</code></dt>
     <dd>
      <p class="para">
       区間の上限
      </p>
     </dd>
    
    
     <dt><code class="parameter">boundary</code></dt>
     <dd>
      <p class="para">
       区間の境界を戻り値に含めるかどうかを指定します。
      </p>
     </dd>
    
   </dl>
  </p>
 </div>


 <div class="refsect1 returnvalues" id="refsect1-random-randomizer.getfloat-returnvalues">
  <h3 class="title">戻り値</h3>
  <p class="para">
   <code class="parameter">min</code>,
   <code class="parameter">max</code>, <code class="parameter">boundary</code>
   で指定した区間に一様に分散した float の中から、等確率に選んだ値を返します。
   <code class="parameter">min</code> と <code class="parameter">max</code>
   が戻り値に含まれるかどうかは、
   <code class="parameter">boundary</code> の値次第です。
  </p>
 </div>


 <div class="refsect1 errors" id="refsect1-random-randomizer.getfloat-errors">
  <h3 class="title">エラー / 例外</h3>
  <ul class="itemizedlist">
   <li class="listitem">
    <span class="simpara">
     <code class="parameter">min</code> の値が有限(<span class="function"><a href="function.is-finite.php" class="function">is_finite()</a></span>)でない場合、
     <span class="classname"><a href="class.valueerror.php" class="classname">ValueError</a></span> がスローされます。
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     <code class="parameter">max</code> の値が有限(<span class="function"><a href="function.is-finite.php" class="function">is_finite()</a></span>)でない場合、
     <span class="classname"><a href="class.valueerror.php" class="classname">ValueError</a></span> がスローされます。
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     指定された区間に何も値が含まれない場合、
     <span class="classname"><a href="class.valueerror.php" class="classname">ValueError</a></span> がスローされます。
    </span>
   </li>
   
 <li class="listitem">
  <span class="simpara">
    <a href="class.random-randomizer.php#random-randomizer.props.engine" class="link"><code class="literal">Random\Randomizer::$engine</code></a> に存在する
    <span class="methodname"><a href="random-engine.generate.php" class="methodname">Random\Engine::generate()</a></span> メソッド
    がスローした、あらゆる <span class="classname"><a href="class.throwable.php" class="classname">Throwable</a></span> がスローされます。
  </span>
 </li>

  </ul>
 </div>


 <div class="refsect1 examples" id="refsect1-random-randomizer.getfloat-examples">
  <h3 class="title">例</h3>
  <div class="example" id="example-1">
   <p><strong>例1 <span class="methodname"><strong>Random\Randomizer::getFloat()</strong></span> の例</strong></p>
   <div class="example-contents">
<div class="annotation-interactive phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br />$randomizer </span><span style="color: #007700">= new </span><span style="color: #0000BB">\Random\Randomizer</span><span style="color: #007700">();<br /><br /></span><span style="color: #FF8000">// 緯度の粒度は経度の粒度の2倍であることに注意しましょう。<br />//<br />// 緯度については、-90 から 90 の値をとります<br />// 経度については、180 という値は取れますが、-180 は取れません。<br />// なぜなら、-180 と 180 は同じ経度を指しているからです。<br /></span><span style="color: #0000BB">printf</span><span style="color: #007700">(<br />    </span><span style="color: #DD0000">"Lat: %+.6f Lng: %+.6f"</span><span style="color: #007700">,<br />    </span><span style="color: #0000BB">$randomizer</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">getFloat</span><span style="color: #007700">(-</span><span style="color: #0000BB">90</span><span style="color: #007700">, </span><span style="color: #0000BB">90</span><span style="color: #007700">, </span><span style="color: #0000BB">\Random\IntervalBoundary</span><span style="color: #007700">::</span><span style="color: #0000BB">ClosedClosed</span><span style="color: #007700">),<br />    </span><span style="color: #0000BB">$randomizer</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">getFloat</span><span style="color: #007700">(-</span><span style="color: #0000BB">180</span><span style="color: #007700">, </span><span style="color: #0000BB">180</span><span style="color: #007700">, </span><span style="color: #0000BB">\Random\IntervalBoundary</span><span style="color: #007700">::</span><span style="color: #0000BB">OpenClosed</span><span style="color: #007700">),<br />);<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

   <div class="example-contents"><p>上の例の出力は、
たとえば以下のようになります。</p></div>
   <div class="example-contents screen">
<div class="annotation-interactive examplescode"><pre class="examplescode">Lat: +69.244304 Lng: -53.548951</pre>
</div>
   </div>
  </div>
 </div>


 <div class="refsect1 notes" id="refsect1-random-randomizer.getfloat-notes">
  <h3 class="title">注意</h3>
  <blockquote class="note"><p><strong class="note">注意</strong>: 
   <p class="para">
    このメソッドは、望ましい振る舞いを得るために
    <a href="https://dl.acm.org/doi/10.1145/3503512" class="link external">&raquo;&nbsp;
     Drawing Random Floating-Point Numbers from an Interval.
     Frédéric Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022
    </a>
    に記されている γ-section アルゴリズムを実装しています。
   </p>
  </p></blockquote>
  <div class="caution"><strong class="caution">警告</strong>
   <p class="para">
    アンダーフローは、γ-section アルゴリズムでは意図的に処理されないままになっています。
    そのため、浮動小数点数の非正規化数の範囲にある境界を持つ区間、
    つまり絶対値がおよそ
    <code class="literal">2<sup class="superscript">-1020</sup></code>（約 <code class="literal">8.9e-308</code>）よりも小さい境界を持つ区間では、
    不正な値が返される可能性があります。
   </p>
  </div>
 </div>


 <div class="refsect1 seealso" id="refsect1-random-randomizer.getfloat-seealso">
  <h3 class="title">参考</h3>
  <ul class="simplelist">
   <li><span class="methodname"><a href="random-randomizer.nextfloat.php" class="methodname" rel="rdfs-seeAlso">Random\Randomizer::nextFloat()</a> - 半開区間 [0.0, 1.0) から、float の値を取得する</span></li>
   <li><span class="methodname"><a href="random-randomizer.getint.php" class="methodname" rel="rdfs-seeAlso">Random\Randomizer::getInt()</a> - 等確率に選ばれる整数を取得する</span></li>
  </ul>
 </div>


</div><?php manual_footer($setup); ?>