Renvoie un flottant uniformément sélectionné

# Random\Randomizer::getFloat

(PHP 8 >= 8.3.0)

Random\Randomizer::getFloat — Renvoie un flottant uniformément sélectionné

### Description

public function Random\Randomizer::getFloat(float `$min`, float `$max`, Random\IntervalBoundary `$boundary` = `Random\IntervalBoundary::ClosedOpen`): float

Renvoie un flottant uniformément sélectionné et équidistribué d'un intervalle demandé.

À cause de la précision limitée, tous les nombres réels ne peuvent pas être exactement représentés en tant que flottants. Si un nombre ne peut pas être représenté exactement, il est arrondi au nombre exactement représentable le plus proche. De plus, les flottants ne sont pas également denses sur toute la ligne des nombres. Parce que les flottants utilisent un exposant binaire, la distance entre deux flottants voisins double à chaque puissance de deux. En d'autres termes : Il y a le même nombre de flottants représentables entre `1.0` et `2.0` qu'entre `2.0` et `4.0`, `4.0` et `8.0`, `8.0` et `16.0`, et ainsi de suite.

Sélectionner un nombre aléatoire dans un intervalle arbitraire, par exemple en divisant deux entiers, pourrait entraîner une distribution biaisée pour cette raison. L'arrondi nécessaire fera que certains flottants seront retournés plus souvent que d'autres, en particulier autour des puissances de deux lorsque la densité des flottants change.

Random\Randomizer::getFloat() implémente un algorithme qui renverra un flottant uniformément sélectionné à partir de l'ensemble le plus large possible de flottants exactement représentables et équidistribués dans l'intervalle demandé. La distance entre les flottants sélectionnables (« pas ») correspond à la distance entre les flottants avec la plus faible densité, c'est-à-dire la distance entre les flottants aux limites de l'intervalle avec la plus grande valeur absolue. Cela signifie que tous les flottants représentables dans un intervalle donné peuvent ne pas être retournés si l'intervalle traverse une ou plusieurs puissances de deux. Le pas commencera à la limite de l'intervalle avec la plus grande valeur absolue pour garantir que les pas s'alignent avec les flottants exactement représentables.

Les limites d'intervalle fermées seront toujours incluses dans l'ensemble des flottants sélectionnables. Donc si la taille de l'intervalle n'est pas un multiple exact du pas et que la limite avec la plus petite valeur absolue est une limite fermée, la distance entre cette limite et son flottant le plus proche sera plus petite que le pas.

Attention

Le post-traitement des flottants retournés risque de casser l'équidistribution uniforme, car les flottants intermédiaires dans une opération mathématique subissent un arrondi implicite. L'intervalle demandé doit correspondre le plus étroitement possible à l'intervalle souhaité et l'arrondi ne doit être effectué qu'en tant qu'opération explicite juste avant d'afficher le nombre sélectionné à un utilisateur.

#### Explications de l'algorithme en utilisant des valeurs d'exemple

Pour donner un exemple de fonctionnement de l'algorithme, considérons une représentation en virgule flottante qui utilise une mantisse de 3 bits. Ceci est capable de représenter 8 valeurs flottantes différentes entre les puissances de deux consécutives. Cela signifie qu'entre `1.0` et `2.0` tous les pas de taille `0.125` sont exactement représentables et entre `2.0` et `4.0` tous les pas de taille `0.25` sont exactement représentables. En réalité, les flottants de PHP utilisent une mantisse de 52 bits et peuvent représenter 252 valeurs différentes entre chaque puissance de deux. Cela signifie que

- `1.0`
- `1.125`
- `1.25`
- `1.375`
- `1.5`
- `1.625`
- `1.75`
- `1.875`
- `2.0`
- `2.25`
- `2.5`
- `2.75`
- `3.0`
- `3.25`
- `3.5`
- `3.75`
- `4.0`

sont les flottants exactement représentables entre `1.0` et `4.0`.

Maintenant considérons que `$randomizer->getFloat(1.625, 2.5, IntervalBoundary::ClosedOpen)` est appelé, c'est-à-dire qu'un flottant aléatoire commençant à `1.625` jusqu'à, mais sans inclure, `2.5` est demandé. L'algorithme détermine d'abord le pas à la limite avec la plus grande valeur absolue (`2.5`). Le pas à cette limite est `0.25`.

Il est à noter que la taille de l'intervalle demandé est `0.875`, qui n'est pas un multiple exact de `0.25`. Si l'algorithme commençait à marcher à la limite inférieure `1.625`, il rencontrerait `2.125`, qui n'est pas exactement représentable et subirait un arrondi implicite. Donc l'algorithme commence à marcher à la limite supérieure `2.5`. Les valeurs sélectionnables sont :

- `2.25`
- `2.0`
- `1.75`
- `1.625`

`2.5` n'est pas inclus, car la limite supérieure de l'intervalle demandé est une limite ouverte. `1.625` est inclus, même si sa distance à la valeur la plus proche `1.75` est `0.125`, qui est plus petite que le pas déterminé précédemment de `0.25`. La raison pour laquelle c'est le cas est que l'intervalle demandé est fermé à la limite inférieure (`1.625`) et les limites fermées sont toujours incluses.

Finalement l'algorithme sélectionne uniformément une des quatre valeurs sélectionnables au hasard et la renvoie.

##### Pourquoi diviser deux entiers ne fonctionne pas

Dans l'exemple précédent, il y a huit nombres flottants représentables entre chaque sous-intervalle délimité par une puissance de deux. Pour donner un exemple de pourquoi diviser deux entiers ne fonctionnerait pas bien pour générer un flottant aléatoire, considérons qu'il y a 16 nombres flottants uniformément distribués dans l'intervalle ouvert à droite de `0.0` jusqu'à, mais sans inclure, `1.0`. La moitié d'entre eux sont les huit valeurs exactement représentables entre `0.5` et `1.0`, l'autre moitié sont les valeurs entre `0.0` et `1.0` avec un pas de `0.0625`. Ces valeurs peuvent facilement être générées en divisant un entier aléatoire entre `0` et `15` par `16` pour obtenir l'une des valeurs suivantes :

- `0.0`
- `0.0625`
- `0.125`
- `0.1875`
- `0.25`
- `0.3125`
- `0.375`
- `0.4375`
- `0.5`
- `0.5625`
- `0.625`
- `0.6875`
- `0.75`
- `0.8125`
- `0.875`
- `0.9375`

Ce flottant aléatoire pourrait être mis à l'échelle à l'intervalle ouvert à droite de `1.625` jusqu'à, mais sans inclure, `2.75` en le multipliant par la taille de l'intervalle (`0.875`) et en ajoutant le minimum `1.625`. Cette transformation affine donnerait les valeurs suivantes :

- `1.625` arrondie à `1.625`
- `1.679` arrondie à `1.625`
- `1.734` arrondie à `1.75`
- `1.789` arrondie à `1.75`
- `1.843` arrondie à `1.875`
- `1.898` arrondie à `1.875`
- `1.953` arrondie à `2.0`
- `2.007` arrondie à `2.0`
- `2.062` arrondie à `2.0`
- `2.117` arrondie à `2.0`
- `2.171` arrondie à `2.25`
- `2.226` arrondie à `2.25`
- `2.281` arrondie à `2.25`
- `2.335` arrondie à `2.25`
- `2.390` arrondie à `2.5`
- `2.445` arrondie à `2.5`

Il convient de noter comment la limite supérieure de `2.5` serait retournée, malgré le fait que ce soit une limite ouverte et donc exclue. Il est également à noter comment `2.0` et `2.25` sont deux fois plus susceptibles d'être retournés par rapport aux autres valeurs.

### Liste de paramètres

`min`

  La limite inférieure de l'intervalle.

`max`

  La limite supérieure de l'intervalle.

`boundary`

  Spécifie si les limites de l'intervalle sont des valeurs de retour possibles.

### Valeurs de retour

Une valeur flottante uniformément sélectionnée et équidistribuée de l'intervalle spécifié par `min`, `max` et `boundary`. Le fait que `min` et `max` soient des valeurs de retour possibles dépend de la valeur de `boundary`.

### Erreurs / Exceptions

- Si la valeur de `min` n'est pas finie (is_finite()), une ValueError sera lancée.
- Si la valeur de `max` n'est pas finie (is_finite()), une ValueError sera lancée.
- Si l'intervalle demandé ne contient aucune valeur, une ValueError sera lancée.
- Tout Throwables lancé par la méthode Random\Engine::generate() du `Random\Randomizer::$engine` sous-jacent.

### Exemples

Exemple #1 Exemple de Random\Randomizer::getFloat()

    <?php
    $randomizer = new \Random\Randomizer();

    // Il est à noter que la granularité de la latitude est le double
    // de la granularité de la longitude.
    //
    // Pour la latitude, la valeur peut être à la fois -90 et 90.
    // Pour la longitude, la valeur peut être 180, mais pas -180, car
    // -180 et 180 font référence à la même longitude.
    printf(
        "Lat: %+.6f Lng: %+.6f",
        $randomizer->getFloat(-90, 90, \Random\IntervalBoundary::ClosedClosed),
        $randomizer->getFloat(-180, 180, \Random\IntervalBoundary::OpenClosed),
    );
    ?>

Résultat de l'exemple ci-dessus est similaire à :

    Lat: +69.244304 Lng: -53.548951

### Notes

> Note:

> Cette méthode implémente l'algorithme de la section γ tel que publié dans » Drawing Random Floating-Point Numbers from an Interval. Frédéric Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022 pour obtenir les propriétés comportementales souhaitées.

Attention

Le sous-dépassement (underflow) est intentionnellement laissé non géré dans l'algorithme de la section γ. Cela peut entraîner le retour de valeurs incorrectes pour les intervalles dont les limites se trouvent dans la plage sous-normale des nombres à virgule flottante, c'est-à-dire pour des limites dont la valeur absolue est inférieure à environ `2-1020` (environ `8.9e-308`).

### Voir aussi

- Random\Randomizer::nextFloat() - Renvoie un flottant sélectionné de l'intervalle ouvert à droite [0.0, 1.0)
- Random\Randomizer::getInt() - Renvoie un entier sélectionné de manière uniforme

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open