Γιατί η rand() + rand() παράγει αρνητικούς αριθμούς;

Γιατί η rand() + rand() παράγει αρνητικούς αριθμούς;

rand() ορίζεται να επιστρέφει έναν ακέραιο μεταξύ του 0 και RAND_MAX .

rand() + rand()

θα μπορούσε να ξεχειλίσει. Αυτό που παρατηρείτε είναι πιθανόν αποτέλεσμα απροσδιόριστης συμπεριφοράς που προκαλείται από υπερχείλιση ακεραίων.


Το πρόβλημα είναι η προσθήκη. rand() επιστρέφει ένα int τιμή του 0...RAND_MAX . Έτσι, αν προσθέσετε δύο από αυτά, θα λάβετε έως και RAND_MAX * 2 . Εάν αυτό υπερβαίνει το INT_MAX , το αποτέλεσμα της προσθήκης υπερχειλίζει το έγκυρο εύρος an int μπορεί να κρατήσει. Η υπερχείλιση υπογεγραμμένων τιμών είναι απροσδιόριστη συμπεριφορά και μπορεί να οδηγήσει στο πληκτρολόγιό σας να σας μιλάει σε ξένες γλώσσες.

Καθώς δεν υπάρχει κανένα κέρδος εδώ στην προσθήκη δύο τυχαίων αποτελεσμάτων, η απλή ιδέα είναι απλώς να μην το κάνετε. Εναλλακτικά, μπορείτε να μεταφέρετε κάθε αποτέλεσμα στο unsigned int πριν από την πρόσθεση, εάν αυτό μπορεί να κρατήσει το άθροισμα. Ή χρησιμοποιήστε μεγαλύτερο τύπο. Σημειώστε ότι long δεν είναι απαραίτητα ευρύτερο από το int , το ίδιο ισχύει για το long long εάν int είναι τουλάχιστον 64 bit!

Συμπέρασμα:Απλώς αποφύγετε την προσθήκη. Δεν παρέχει περισσότερη «τυχαιότητα». Εάν χρειάζεστε περισσότερα bit, μπορείτε να συνδέσετε τις τιμές sum = a + b * (RAND_MAX + 1) , αλλά αυτό πιθανότατα απαιτεί μεγαλύτερο τύπο δεδομένων από το int .

Καθώς ο λόγος που αναφέρατε είναι να αποφύγετε ένα μηδενικό αποτέλεσμα:Αυτό δεν μπορεί να αποφευχθεί προσθέτοντας τα αποτελέσματα δύο rand() κλήσεις, καθώς και τα δύο μπορεί να είναι μηδενικά. Αντίθετα, μπορείτε απλώς να αυξήσετε. Εάν RAND_MAX == INT_MAX , αυτό δεν μπορεί να γίνει στο int . Ωστόσο, (unsigned int)rand() + 1 θα γίνει πολύ, πολύ πιθανό. Πιθανό (όχι οριστικά), γιατί απαιτεί UINT_MAX > INT_MAX , το οποίο ισχύει για όλες τις υλοποιήσεις που γνωρίζω (η οποία καλύπτει αρκετές ενσωματωμένες αρχιτεκτονικές, DSP και όλες τις πλατφόρμες επιτραπέζιων υπολογιστών, κινητών και διακομιστών των τελευταίων 30 ετών).

Προειδοποίηση:

Αν και είναι ήδη πασπαλισμένο σε σχόλια εδώ, σημειώστε ότι η προσθήκη δύο τυχαίων τιμών δεν λάβετε μια ομοιόμορφη κατανομή, αλλά μια τριγωνική κατανομή σαν να ρίχνετε δύο ζάρια:για να λάβετε 12 (δύο ζάρια) και τα δύο ζάρια πρέπει να δείχνουν 6 . για 11 υπάρχουν ήδη δύο πιθανές παραλλαγές:6 + 5 ή 5 + 6 , κ.λπ.

Άρα, η προσθήκη είναι επίσης κακή από αυτήν την άποψη.

Σημειώστε επίσης ότι τα αποτελέσματα rand() τα προϊόντα δεν είναι ανεξάρτητα το ένα από το άλλο, καθώς δημιουργούνται από μια γεννήτρια ψευδοτυχαίων αριθμών . Σημειώστε επίσης ότι το πρότυπο δεν προσδιορίζει την ποιότητα ή την ομοιόμορφη κατανομή των υπολογισμένων τιμών.


Αυτή είναι μια απάντηση σε μια διευκρίνιση της ερώτησης που έγινε στο σχόλιο αυτής της απάντησης,

Το πρόβλημα ήταν να αποφευχθεί το 0. Υπάρχουν (τουλάχιστον) δύο προβλήματα με την προτεινόμενη λύση. Το ένα είναι, όπως δείχνουν οι άλλες απαντήσεις, ότι rand()+rand() μπορεί να επικαλεστεί απροσδιόριστη συμπεριφορά. Η καλύτερη συμβουλή είναι να μην επικαλείστε ποτέ απροσδιόριστη συμπεριφορά. Ένα άλλο ζήτημα είναι ότι δεν υπάρχει εγγύηση ότι το rand() δεν θα παράγει 0 δύο φορές στη σειρά.

Το παρακάτω απορρίπτει το μηδέν, αποφεύγει την απροσδιόριστη συμπεριφορά και στη συντριπτική πλειονότητα των περιπτώσεων θα είναι ταχύτερη από δύο κλήσεις στο rand() :

int rnum;
for (rnum = rand(); rnum == 0; rnum = rand()) {}
// or do rnum = rand(); while (rnum == 0);