int chance = -5; int rand = arc4random() % 100; // Number from 0 to 99 if (rand <= chance) { // This will never happen NSLog(@"This is... NOT POSSIBLE"); }
Effectivement, cela n'arrive jamais. Mais
int chance = -5; if (arc4random() % 100 <= chance) { NSLog(@"This is... NOT POSSIBLE"); }
Ici, au lieu de le stocker dans une variable, j'ai placé l'expression de nombre random directement dans la condition. Et la condition est remplie (parfois).
Pourquoi donc? Comment puis-je déboguer ce comportement?
Tapez les règles de promotion.
arc4random
renvoie une valeur non signée . Cela signifie que dans votre deuxième cas, le -5
est promu au même type non signé, le transformant en 4294967291
. 4+ milliards est nettement plus grand que n'importe quel nombre 0-99!
Passons en revue ce qui se passe dans vos deux exemples.
De votre premier exemple, dans cette ligne:
int rand = arc4random() % 100;
arc4random()
renvoie une valeur non signée. Alors ça ressemble à:
int rand = someUnsignedNumber % 100;
Le 100
est un int signé, donc il est promu au même type que someUnsignedNumber
, et l'opération %
est appliquée. Après cela, vous avez:
int rand = someUnsignedNumberBetween0And99;
L'affectation de ce nombre non signé à int rand
le ramène à un nombre signé. Votre comparaison se poursuit comme prévu.
Dans le deuxième exemple, vous avez cette ligne:
if (arc4random() % 100 <= chance)
Les mêmes choses se produisent avec arc4random() % 100
, ce qui donne quelque chose comme:
if (someUnsignedNumberBetween0And99 <= chance)
Mais ici, le chance
est un nombre signé. Il obtient promu, en changeant sa valeur comme décrit ci-dessus, et vous vous retrouvez avec le comportement étrange que vous voyez.
Silly, système de type stupide de C … Si vous lisez la page man pour arc4random()
, vous découvrez que son prototype est
u_int32_t arc4random(void);
Il renvoie donc un entier non signé .
En comparant son résultat – unsigned – avec un autre entier, le non-signe "gagne": l'autre valeur ( -5
) est promue au type non signé ( u_int32_t
dans ce cas), elle survole (puisque l'entier non signé "underflow" est conçu pour travaillez comme ceci en C – vous obtiendrez 2 ^ 32 - 5
) et donc une comparaison "erronée" (c'est-à-dire se comporter comme inattendue) se produit.
Lorsque vous atsortingbuez explicitement la valeur à une variable int
(c'est-à-dire signée ), cette promotion ne se produit pas car la comparaison se fait entre deux types signés, elle est donc évaluée comme vous le souhaitez.