<?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 => 'ru',
  ),
  'this' => 
  array (
    0 => 'random-randomizer.getfloat.php',
    1 => 'Random\\Randomizer::getFloat',
    2 => 'Получает равномерно выбранное число с плавающей точкой',
  ),
  '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' => 'ru',
    '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">Получает равномерно выбранное число с плавающей точкой</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">
   Из-за фундаментальных ограничений двоичной архитектуры
   не каждое вещественное число возможно точно представить в памяти в виде числа с плавающей точкой.

   Числа, которые невозможно представить точно, округляются до ближайшего
   точного представления.

   На числовой оси плотность чисел с плавающей точкой неравномерна.

   В двоичном представлении чисел с плавающей точкой экспонента удваивает длину каждого следующего интервала:
   <code class="literal">[1.0, 2.0)</code>, <code class="literal">[2.0, 4.0)</code>, <code class="literal">[4.0, 8.0)</code>, <code class="literal">[8.0, 16.0)</code>,….
   При этом из-за фиксированного количества битов для хранения мантиссы
   количество значений внутри отдельного интервала остаётся одинаковым,
   поэтому расстояние между соседними значениями в очередном интервале удваивается.
  </p>
  <p class="para">
   Из-за неравномерной плотности чисел с плавающей точкой на числовой оси
   выбор случайного числа в пределах произвольно заданного интервала, —
   например, путём деления двух целых чисел, — смещает распределение.

   Из-за вынужденного округления одни числа с плавающей точкой
   возвращаются чаще остальных, а вблизи границ интервалов, по обе стороны от которых плотность
   чисел с плавающей точкой меняется скачкообразно, эффект усиливается.
  </p>
  <p class="para">
   Метод <span class="methodname"><strong>Random\Randomizer::getFloat()</strong></span> работает на базе алгоритма,
   который равномерно выбирает значение из наибольшего возможного в пределах заданного интервала
   подмножества точно представимых равнораспределённых чисел с плавающей точкой.

   «Шагом» для выбора случайного числа с плавающей точкой становится расстояние между значениями
   двоичного интервала с наименьшей плотностью, которому принадлежит граница с бо́льшим абсолютным значением.

   Поэтому из заданного аргументами интервала вернётся не каждое представимое число с плавающей точкой,
   если интервал пересечёт одну или больше степеней двойки.

   Проход равноразмерными шагами начнётся с правой границы самого разреженного интервала,
   чтобы гарантировать совпадение шагов c точно представимыми числами с плавающей точкой.
  </p>
  <p class="para">
   Закрытые границы интервалов принудительно включаются в набор значений с плавающей точкой для случайного выбора.

   Расстояние между границей с меньшим абсолютным значением
   и ближайшим числом с плавающей точкой окажется меньше размера шага,
   если размер интервала не кратен размеру шага и граница с меньшим абсолютным значением закрыта.
  </p>
  <div class="caution"><strong class="caution">Предостережение</strong>
   <p class="para">
    Постобработка чисел с плавающей точкой, которые возвращает метод, наверняка нарушит равномерное равнораспределение,
    поскольку промежуточные плавающие значения внутри математических операций неявно округляются,
    поэтому интервал, в пределах которого требуется получить случайные значения, сразу предельно точно задают аргументами,
    а округляют случайные значения только сознательно — непосредственно перед выводом случайного числа пользователю.
   </p>
  </div>
  <div class="refsect2 unknown-28" id="refsect2-random-randomizer.getfloat-unknown-28">
   <h4 class="title">Объяснение алгоритма с примерами значений</h4>
   <p class="para">
    Работу алгоритма проиллюстрирует пример,
    в котором для представления мантиссы числа с плавающей точкой отводится только 3 бита.

    Между соседними степенями двойки в трёхбитном поле мантиссы
    возможно представить только 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 предусмотрели 52-битное поле,
    в котором между соседними степенями двойки возможно представить
    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">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">
    В заключение из четырёх доступных значений алгоритм равномерно выбирает и возвращает одно случайное значение.
   </p>
   <div class="refsect3 unknown-31" id="random-randomizer.getfloat.affine-transformation">
    <h5 class="title">Почему деление двух целых чисел не работает</h5>
    <p class="para">
     В предыдущем примере внутри каждого подынтервала с границами по степеням двойки
     содержится восемь представимых чисел с плавающей точкой.

     Следующий пример объяснит, почему деление двух целых чисел не работает для генерации
     случайного числа с плавающей точкой. Пусть в открытом справа интервале
     от <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>, другая —
     ещё восемь значении в диапазоне от <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">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> в два раза выше возврата других значений.
    </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>.

   Параметр <code class="parameter">boundary</code> определяет, войдут ли значения <code class="parameter">min</code> и <code class="parameter">max</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">
  Любые <span class="classname"><a href="class.throwable.php" class="classname">Throwable</a></span>, выбрасываемые методом <span class="methodname"><a href="random-engine.generate.php" class="methodname">Random\Engine::generate()</a></span>
  базового <a href="class.random-randomizer.php#random-randomizer.props.engine" class="link"><code class="literal">Random\Randomizer::$engine</code></a>.
 </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 /><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">// Обратите внимание, что степень детализации по широте в два раза выше<br />// степени детализации по долготе.<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">"Широта: %+.6f Долгота: %+.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 />);</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">Широта: +69.244304 Долгота: -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>;
    за счёт поведенческих характеристик алгоритма метод генерирует статистически достоверные случайные значения.
   </p>
  </p></blockquote>
  <div class="caution"><strong class="caution">Предостережение</strong>
   <p class="para">
    В алгоритме γ-секции сознательно не обрабатываются близкие к нулю результаты вычислений, которые выходят за пределы нормализованных значений,
    поэтому для интервалов с границами в субнормальном диапазоне чисел с плавающей точкой —
    границ с абсолютным значением меньше приблизительно
    <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)</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); ?>