2016-08-04 11 views
6

Ich versuche, ein C-Programm zu verstehen, die mit der LinieBedeutung von ((float) rand()/(float) ((1 << 31) - 1))

#define random     ((float) rand()/(float)((1 << 31) - 1)) 
eine .h-Datei enthält

Das C-Programm enthält auch <math.h>.

Meine Vermutung ist, dass dies einfach eine Zufallszahl aus einer gleichmäßigen Verteilung auf dem Intervall [0,1] erzeugt; ist das richtig?

+0

undefiniertes Verhalten auf allen Plattformen mit 'int' mit nicht mehr als 32 Bits) sein würde. Alles kann passieren, Fall geschlossen - nächste bitte! – Olaf

+1

Mögliches Duplikat von [Ist 1 << 31 in C gut definiert, wenn sizeof (int) == 4] (https://stackoverflow.com/questions/45268467/is-131- well-defined-in-c-when- sizeofint-4) –

Antwort

10

Scheinbar ja. Aber es ist falsch in zwei Hauptwege:

  1. Verwenden RAND_MAX statt. Dafür ist es da. Es könnte viel kleiner als 1 << 31 - 1 sein.

  2. 1 << 31 geben Ihnen undefiniertes Verhalten auf einer Plattform mit einem 32-Bit-int oder weniger, was bemerkenswert ist weit verbreitet. Tu das nicht!

Beachten Sie, wenn Sie nicht wollen, immer den Wert 1, sich zu erholen (wie es oft der Fall ist), dann RAND_MAX + 1.0 auf den Nenner verwenden. Die 1.0 erzwingt die Auswertung in Fließkommawerten: Sie riskieren, dass ein ganzzahliger Typ überläuft, wenn Sie RAND_MAX + 1 schreiben.

+0

Es würde auch UB auf Plattformen mit mehr als 16, aber weniger als 33 Bits aufrufen. Und 'RAND_MAX + 1' ruft sehr wahrscheinlich UB auf allen Plattformen auf. – Olaf

+0

Wenn es Ihnen nichts ausmacht habe ich die Antwort bearbeitet. Beides sind sehr gute Punkte. Vielen Dank. –

+0

Aber ist 'RAND_MAX + 1.0' nicht ein' double' Typ? –

5

Die Funktion rand gibt einen Wert zwischen 0 und RAND_MAX zurück. Dieses Programm geht davon aus, dass RAND_MAX 2^31 - 1 ist und das Ergebnis durch diese Zahl dividiert.

Also, wenn die obige Annahme wahr ist, dann gibt dieses Makro eine Zahl von [0,1]. Es ist nicht so sehr eine gleichmäßige Zufallsverteilung, sondern ein "pseudo-zufälliger" Wert.

Zumindest ist es das, was es ist angenommen zu tun. Dieser Ausdruck (1 << 31) ruft ein undefiniertes Verhalten auf (unter der Annahme, dass int 32-Bit oder weniger ist), da die Konstante 1 den Typ int hat und bei einer Verschiebung nach links um 31 außerhalb des Bereichs von int liegt. In der Praxis wird, wenn Zweierkomplementdarstellung verwendet wird, einige Compiler diese Verschiebung erlauben, und dann wird die nachfolgende -1 sie wieder in den Bereich bringen, aber das kann nicht davon abhängig sein.

Dieses undefinierte Verhalten kann vermieden werden, indem (1U << 31) verwendet wird, wodurch die Konstante 1 den Typ unsigned int hat, so dass die Verschiebung innerhalb des Bereichs liegt. Eine bessere Option ist, die Verschiebung zu vergessen und zu subtrahieren und einfach 0x7fffffff zu verwenden.

#define random ((float)rand()/RAND_MAX) 

Aber es gibt noch ein Problem:

Aber für maximale Portabilität, sollte es wie folgt definiert werden. A float hat typischerweise eine 23-Bit-Mantisse. Wenn rand einen 32-Bit-Wert zurückgibt, erhalten Sie keine gute Zahlenverteilung.Bessere Nutzung Verwendung double, das ein 52-Bit-Mantisse hat:

#define random ((double)rand()/RAND_MAX) 
+0

Sie sehen keine UB? – Olaf

+0

@Olaf Richtig. Details dazu hinzugefügt. – dbush

+0

Diese Verschiebung ist problematisch - im besten Fall. Bei Implementierungen mit weniger als 32 Bits wird immer noch UB (Verschiebeanzahl> = Bitbreite) aufgerufen. Der "RAND_MAX" -Ansatz ist besser. Die Verwendung von "float" verliert jedoch sehr wahrscheinlich die Genauigkeit und begrenzt die Verteilung auf typischerweise nicht mehr als 23 Bits (Mantisse von IEEE "float"). – Olaf

1

Meine Vermutung ist, dass dies einfach eine Zufallszahl aus einer Gleichverteilung erzeugt auf dem Intervall [0,1]; ist das richtig?

Nr. Code kann nie 1.0f noch einheitliche Ergebnisse zurückgeben.

#define random ((float) rand()/(float)((1 << 31) - 1)) hat viele Probleme. Es ist ein schwacher Code.


verloren Präzision: Typische float hat etwa 24-Bit-Präzision. Konvertiert man das Ergebnis von rand(), wenn es 24 Bits überschreitet, ergibt sich eine float, die sich aufgrund von Rundungen vom Originalwert unterscheiden kann. Dies schwächt/zerstört die Einheitlichkeit der Zufallszahlengenerierung. Verschiedene rand() Ergebnisse werden mit der gleichen Antwort kommen. Siehe auch @Olaf

Eine Lösung für dies ist problematisch, da OP offenbar eine einheitliche Zufallszahl aus dem Satz will [0, 1/2147483648, 2/2147483648 ... 2147483647/2147483648], die die wahrscheinliche Genauigkeit gegeben ist nicht möglich Grenzen von float.


Das Schlimmste ist, dass (1 << 31)undefinierten Verhalten UB ist, es sei denn int lang mindestens 33 Bits. Eine 1 in die Vorzeichenposition zu schieben ist UB. C11dr §6.5.7 4.

Um UB zu vermeiden, verwenden Sie ((1ul << 31) - 1).

Noch ist die Verwendung der magischen Zahl ((1ul << 31) - 1) nicht so robust wie das Basieren der Fraktion auf RAND_MAX.

Weitere (float) ((1ul << 31) - 1) kann unter Verlust der Genauigkeit leiden, wie oben beschrieben, da es den Wert 2147483648.0f und nicht das nicht erhältliche 2147483647.0f bildet. OP-Code kann nie ein 1.0f erzeugen.


Ich vermute, OP benötigt wirklich ein [0..1) Ergebnis und nicht [0..1]. Beide sind unten.

// generate a `double` in the range [0 ... 1) 
#define random0to_almost1 (rand()/(RAND_MAX + 1.0)) 
// or 
// generate a `double` in the range [0 ... 1] 
#define random0to1 (rand()/(RAND_MAX + 0.0)) 

Beachten Sie, dass dies wie Original-Code des OP leidet, sollte die Genauigkeit der double (typisch 53 Bit) durch RAND_MAX Bedürfnisse überschritten.

Um zu bewältigen, ist ein mildernder Schritt zu versichern, RAND_MAX + 1.0 ist genau getan. In der extrem gebräuchlichen, aber nicht C angegeben, ist RAND_MAX eine Power-of-2_minus_1. So RAND_MAX/2 + 1 ist ein int und ein genaue Leistung von 2. Die Umwandlung von int zu double ist sicherlich genau.

#define random0to_almost1 (rand()/(2.0*(RAND_MAX/2 + 1))) 

A float Lösung

// This value is platform dependent, but very common 
// Do not a a highly portable generation method yet. 
#define FLT_POWER2_INTEGER_LIMIT (1ul << 24) 


#define random0to_almost1 (\ 
    (rand() % FLT_POWER2_INTEGER_LIMIT)/\ 
    (RAND_MAX % FLT_POWER2_INTEGER_LIMIT + 1.0f) \ 
    ) 
+1

Die Umwandlung einer Potenz von 2 "int" -Werten in "double" ist möglicherweise nicht exakt, wenn "FLT_RADIX" keine Potenz von 2 ist. –

+0

@Ian Abbott Ja. C erlaubt eine 'FLT_RADIX' von' 2,3,4,5 ... 'Ich muss noch auf eine' FLT_RADIX' anders als '2,10,16' stoßen - natürlich kann die Zukunft anders sein. Sicherlich sind Werte von '2,4,8,16, ..' kein Problem mit der obigen Antwort. Mit der Basis 10 hat die Verwendung der obigen Methode immer noch Vorteile, aber sie kann _exactness_ nicht erreichen. Sollten Sie wissen, dass heute Systeme ohne Basis 2 im Einsatz sind, wären Sie am meisten daran interessiert, von ihnen zu hören. – chux