J'ai toujours pensé que les nombres aléatoires se situaient entre zéro et un, sans 1
c'est-à-dire qu'il s'agit de nombres de l'intervalle semi-ouvert [0,1]. Les documentation sur cppreference.com de std::generate_canonical
le confirme.
Cependant, lorsque j'exécute le programme suivant :
#include <iostream>
#include <limits>
#include <random>
int main()
{
std::mt19937 rng;
std::seed_seq sequence{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
rng.seed(sequence);
rng.discard(12 * 629143 + 6);
float random = std::generate_canonical<float,
std::numeric_limits<float>::digits>(rng);
if (random == 1.0f)
{
std::cout << "Bug!\n";
}
return 0;
}
Le résultat est le suivant :
Bug!
c'est-à-dire qu'il me génère un 1
ce qui cause des problèmes dans l'intégration de mon MC. Ce comportement est-il valable ou y a-t-il une erreur de ma part ? Cela donne le même résultat avec G++ 4.7.3
g++ -std=c++11 test.c && ./a.out
et clang 3.3
clang++ -stdlib=libc++ -std=c++11 test.c && ./a.out
S'il s'agit d'un comportement correct, comment puis-je éviter 1
?
Édition 1 : G++ à partir de git semble souffrir du même problème. Je suis sur
commit baf369d7a57fb4d0d5897b02549c3517bb8800fd
Date: Mon Sep 1 08:26:51 2014 +0000
et en compilant avec ~/temp/prefix/bin/c++ -std=c++11 -Wl,-rpath,/home/cschwan/temp/prefix/lib64 test.c && ./a.out
donne le même résultat, ldd
rendements
linux-vdso.so.1 (0x00007fff39d0d000)
libstdc++.so.6 => /home/cschwan/temp/prefix/lib64/libstdc++.so.6 (0x00007f123d785000)
libm.so.6 => /lib64/libm.so.6 (0x000000317ea00000)
libgcc_s.so.1 => /home/cschwan/temp/prefix/lib64/libgcc_s.so.1 (0x00007f123d54e000)
libc.so.6 => /lib64/libc.so.6 (0x000000317e600000)
/lib64/ld-linux-x86-64.so.2 (0x000000317e200000)
Édition 2 : J'ai signalé ce comportement ici : https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63176
Édition 3 : L'équipe de clang semble être consciente du problème : http://llvm.org/bugs/show_bug.cgi?id=18767
1 votes
Cela semble en effet erroné.
0 votes
Testé sur g++, clang et VS2013. Seul VS2013 a réussi Clang et g++ génèrent 1.0. Il semble que ce soit un double bug : gcc et Clang sont tous les deux dans l'erreur.
0 votes
Le compilateur n'a rien à voir avec le nombre généré, la stdlib est importante - je suppose que vous utilisez libstdc++ pour GCC et Clang. Si vous le pouvez, essayez libc++.
0 votes
C'est intéressant. Quel est le résultat de VS2013 ? Quelle est la différence avec GCC/clang ?
0 votes
Oubliez ce que j'ai dit, VS2013 génère des valeurs >> 1.0.
0 votes
@Xeo : Je ne l'ai pas fait, mais même avec libc++ c'est la même chose.
0 votes
Je ne peux pas reproduire ce problème avec GCC. Comme l'a dit Xeo, ce n'est pas le compilateur en tant que tel, mais la bibliothèque standard. Essayez de la mettre à jour. Ma version plutôt récente n'a pas ce bug.
1 votes
@Niall C'est parfaitement exact. Un simple
if ( random == 1.0f ) { std::printf("Bad\n"); }
aurait suffi.22 votes
David Lively
1.f == 1.f
dans tous les cas (quels sont les cas de figure ? Je n'ai même pas vu de variables dans1.f == 1.f
; il n'y a qu'un seul cas :1.f == 1.f
et c'est invariablementtrue
). Merci de ne pas propager ce mythe. Les comparaisons en virgule flottante sont toujours exactes.1 votes
Il semble que GCC et Clang se soient trompés en produisant la version 1.0 et c'est un bogue qui devrait être signalé. Pour être complet, j'ai essayé le code posté par l'OP avec VS2013 et j'ai obtenu des nombres qui sont >> 1. J'ai cherché un peu et j'ai trouvé que c'est un bug pour VS2013 qui est toujours actif ( rapport de bogue ).
1 votes
Il est clair que vous ne comprenez pas le fonctionnement des nombres à virgule flottante.
15 votes
@DavidLively : Non, ce n'est pas le cas. La comparaison est toujours exacte. Ce sont vos opérandes qui peuvent ne pas être exacts si ils sont calculés et non littéraux.
0 votes
Je me trompe peut-être (je ne suis pas un expert), mais n'y aurait-il pas une plage de valeurs (entre la dernière valeur représentable avant 1,0 et 1,0) qui ne serait pas prise en compte, à moins que 1,0 ne soit occasionnellement affiché comme la valeur arrondie la plus proche ? Je veux parler de la plage [1,0 - epsilon, 1,0]. Cette valeur ne serait-elle pas parfois arrondie à la valeur inférieure (1,0 - epsilon) et parfois à la valeur supérieure (1,0) ?
2 votes
@Galik tout nombre positif inférieur à 1.0 est un résultat valide. 1,0 ne l'est pas. C'est aussi simple que cela. L'arrondi n'est pas pertinent : le code obtient un nombre aléatoire et ne l'arrondit pas.
0 votes
Hm, le GCC de git (mis à jour il y a quelques jours) donne le même résultat, mais je ne suis pas sûr d'utiliser la dernière version de libstdc++. Est-il suffisant de préfixer
./a.out
conLD_LIBRARY_PATH
pointant vers le chemin correspondant ?1 votes
@LightnessRacesinOrbit, un peu de clarté, merci.
1 votes
@cschwan : C'est possible, mais vérifiez auprès de
ldd
.7 votes
@DavidLively il dit qu'il n'y a qu'une seule valeur qui se compare à 1.0. Cette valeur est 1,0. Les valeurs proches de 1,0 ne se comparent pas à 1,0. Peu importe ce que fait la fonction de génération : si elle renvoie 1,0, la comparaison sera égale à 1,0. Si elle ne renvoie pas 1,0, la comparaison ne sera pas égale à 1,0. Votre exemple utilisant
abs(random - 1.f) < numeric_limits<float>::epsilon
vérifie si le résultat est proche de 1,0 ce qui est totalement faux dans ce contexte : il y a des nombres proches de 1,0 qui sont des résultats valables ici, à savoir tous ceux qui sont inférieurs à 1,0.2 votes
@R. Martinho Fernandes D'accord, je l'accepte. Mais pour clarifier mon propos, je parlais de l'arrondi inévitable qui se produit dans l'algorithme de génération de nombres aléatoires en raison des limitations matérielles sur la représentation du nombre réel. Cela signifie que les nombres extrêmement proches de 1,0 ne peuvent pas être représentés du tout ou sont arrondis à (1,0 - epsilon), ce qui donne une surreprésentation de ces valeurs. C'est du moins ce qu'il me semble.
4 votes
@Galik Oui, il y aura des problèmes de mise en œuvre. Mais c'est à l'implémenteur de s'occuper de ces problèmes. L'utilisateur ne doit jamais voir un 1.0, et il doit toujours voir une distribution égale de tous les résultats.
0 votes
@R.MartinhoFernandes Bon, je suis apparemment en train de faire un lundi, le jeudi. Je me trompe souvent. C'est peut-être l'une de ces fois. Je supprime mes commentaires précédents, car ils semblent trompeurs.
0 votes
Le titre de cette question devrait-il être "est-ce que 1.0 est une sortie valide de std::generate_canonical" ? En l'état, la question est plutôt mal posée.
0 votes
Eh bien, cela semble attirer beaucoup de spectateurs ;) - mais oui, vous avez raison.