(PHP 8 >= 8.3.0)
Random\Randomizer::getFloat — Renvoie un flottant uniformément sélectionné
$min
, float $max
, Random\IntervalBoundary $boundary
= Random\IntervalBoundary::ClosedOpen): floatRenvoie un flottant uniformément sélectionné et équidistribué d'un intervalle demandé.
A 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.
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.
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
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.
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
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.
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.
Une valeur flottante uniformément sélectionnée et équidistribuée de l'intervalle spécifié par
min
, max
et boundary
.
Si boundary
est Random\IntervalBoundary::ClosedClosed
,
min
et max
sont des valeurs de retour possibles.
min
n'est pas finie (is_finite()),
une ValueError sera lancée.
max
n'est pas finie (is_finite()),
une ValueError sera lancée.
Random\Randomizer::$engine
sous-jacent.
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
Note:
Cette méthode implémente l'algorithme de la section γ tel que publié dans » Dessiné des nombres flottants aléatoires d'un intervalle. Frédéric Goual pour obtenir les propriétés comportementales souhaitées.