Diskussion:RND

Aus C64-Wiki
Zur Navigation springenZur Suche springen

RND(0) Beispiel[Quelltext bearbeiten]

Zitat: Bei RND(0) ist deutlich ein Muster zu erkennen, da die Funktion nur 256 verschiedene Werte liefert und damit nicht alle 1000 Zeichen auf dem Bildschirm erreicht werden können. von BlackJack.

Die Aussage stimmt, steht aber ein bisschen leer im Raum, wie es nämlich zu der Feststellung kommt. Hier vielleicht ein kleine Hilfe:

Die Mantisse für den Fließkommawerte wird zusammen gestellt aus folgenden Teilen:

+-----------------+-----------------+----------------+---------------+
| höherwertige Bits <-------- Mantisse ---------> niederwertige Bits | 
+-----------------+-----------------+----------------+---------------+
|    M1 ($62)     |     M2 ($63)    |     M3 ($64)   |    M4 ($65)   |
+-----------------+-----------------+----------------+---------------+
|  Timer A low    | Echtzeit 1/10s  |  Timer A high  | Echtzeit 1s   |
+-----------------+-----------------+----------------+---------------+

Solange die Echtzeituhr nicht gestartet wird, sind deren Anteile 0 und die Mantisse nur 65536 Werte vom Timer A (theoretisch) annehmen, wobei der Timer auch nicht alle Werte durchläuft (was nur den Timer-A-high-Anteil angeht), also der Werteumfang sogar noch kleiner ist.

Wenn also der der RND(0)-Wert mit 1000 multipliziert und die anderen Stellen mittels INT() abgeschnitten werden, dann werden von der Mantisse lediglich 10 Bits (*1000 entspricht dem Verschieben von ca. 10 Bits vor das Komma) von den höchstwertigen übernommen, wobei also 8 Bits von Timer A low kommen. Selbst wenn die Echtzeituhr läuft, steuerte diese dann lediglich 2 Bits bei, wobei durch die BCD-Darstellung des 1/10s Wertes gerade die Zehnerziffer immer 0 ist und damit auch die oberen 2 0-Bits die durch die Multiplikation "nach oben" geholt werden.

Somit wandern in den INT(RND(0)*1000) Wert lediglich 8 veränderliche Bits, d.h. es kommen defacto nur 256 Werte zustande. Vermeiden lässt sich das nur, wenn man sich auf eine Multiplikation mit <= 256 beschränkt oder auch die anderen Mantissen heranzieht, was entsprechenden arithmetischen Berechnungsaufwand erfordert, sodass es "billiger" wäre gleich mit TI zu rechnen ...

Wie auch immer, im genannten Beispiel soll aber so gesehen lediglich verdeutlicht werden, dass die unbedachte, speziell sequenzielle Verwendung von RND(0) für einen 256 übersteigenden Wertebereich, keine guten Eigenschaften aufweist. Selbst wenn man sich auf den 256-Bereich beschränkt, hat man dann noch immer den Effekt, dass Abfragen in zeitlich konstanten Abständen eine mehr oder weniger enge Periode bedingt durch den gleichförmigen Timer-Lauf erhält.
--JohannKlasek 09:45, 19. Mär. 2013 (CET)

Vorteil RND(Zahl) gegenüber RND(0)[Quelltext bearbeiten]

Ist RND(1) eigentlich schneller auszuführen als RND(0) oder welchen Vorteil hat es (in den Beispielen) festgelegte Zahlenfolgen zu benutzen ? --FXXS 18:11, 7. Mär 2007 (CET)

Wer sich das ROM für den Befehl RND anschaut, sieht, dass das RND-Argument 0 (Null) den Inhalt des Timers von CIA 1 ($dc04/05) und die Millisekunden und Sekunden der TOD-Uhr ($dc08/09) als Seed selektiert. Ein positives Argument holt sich als Seed den letzten RND-Wert (der steht in $8b bis $8f), multipliziert diesen mit der Konstanten in $e08d (Wert: 11.879.546) und addiert dann noch den Wert der Konstanten in $e092 (3.92767774 mal 10 hoch minus 4). Ein negatives Argument nimmt sich den Wert des Arguments selbst als Seed. Bei allen Methoden steht zum Schluss in $8b bis $8f eine so erzeugte Zufallszahl. Wenn man nun eine immer andere Zahl braucht, würde ich das so machen: Einmal (oder auch immer wieder, gedacht als Initialisierung) z=RND(-TI) (negatives Argument als Seed, Wert von TI ist nicht wirklich vorhersagbar), dann im Programm z=RND(1). Die erste Methode (mit Null als RND-Argument) könnte unter Umständen in die Hose gehen (die Uhr läuft nicht, was der Normalfall ist und/oder der Timer ist aus irgendeinem Grunde abgeschaltet...) TI läuft eigentlich immer irgendwie, wenn auch manchmal mit Unterbrechungen (was noch mehr Zufall ins Spiel bringt). grins --GoDot 12:59, 8. Mär 2007 (CET)

Wenn ich das KERNAL-Listing richtig gelesen haben, dann wird der Timer A von CIA 1 verwendet, der den für das KERNAL-Timing auf IRQ-Basis verwendet wird. Wenn der nicht läuft, läuft auch TI nicht. Dann hat das System ohnehin ein ernstes Problem ... So gesehen macht RND(-TI) und RND(0) am Anfang keinen sonderlichen Unterschied, bis auf den unterschiedlichen konkreten Anfangswert, der zustande kommt. Bei RND(0) beschränkt sich die Auswahl auf 65536 verschiedene Werte, wenn - wie üblich - die Echtzeituhr nicht läuft. Bei TI haben wir eine Auswahl von 62208000 Werten (24*12*60*60*60 Ticks für 24h), wobei sich aber der Wert nur alle 1/60 Sekunden ändert. --JohannKlasek 00:32, 19. Mär. 2013 (CET)

Ach so, deine Frage: Am schnellsten wird RND(.) abgearbeitet. --GoDot 13:01, 8. Mär 2007 (CET)

Für den Fall mit positivem Argument ist es RND(π). Einige Timing-Werte zum Vergleich:
Variante Zeit (s)
RND(0) 4.65
RND(Z) (Z=0) 4.30
RND(.) 4.07
RND(1) 7.91
RND(Z) (Z=1) 7.53
RND(π) 7.13
Für 1000 Durchläufe, Zeiten könne variieren.
--JohannKlasek 00:32, 19. Mär. 2013 (CET)


Funktionsweise von RND(.) ist nun nicht erklärt und der Rest gehört eigentlich auch in den Artikel *g* aber danke für die gute Antwort ---FXXS 22:45, 8. Mär 2007 (CET)
Wenn du in Basic eine Null brauchst, setzt du immer "." (Punkt), das spart das Umwandeln des Zeichens "0" in den Wert 0 beim Parsen, geht also schneller. --GoDot 14:49, 9. Mär 2007 (CET)

Zum Listing RND(0) gegen RND(1)[Quelltext bearbeiten]

Listing ist recht gut. Allerdings sollte man vielleicht überdenken, anstelle von Steuerzeichen CHR$-Codes zu nutzen, wegen dem Ausschluß von Fehlermöglichkeiten.--Jodigi 00:39, 29. Dez. 2008 (CET)

Wertebereich[Quelltext bearbeiten]

"Sie erzeugt eine pseudozufällige Gleitpunktzahl im Bereich zwischen 0.0 und 1.0, wobei diese beiden Zahlen selbst nicht vorkommen." - die 0 kommt eigentlich schon vor, oder? -- 1570 12:10, 7. Sep. 2013 (CEST)

Wenn TimerA und TOD10THS/TODSEC im CIA1 Null enthält müßte bei RND(0) auch Null herauskommen
C64 für Insider ab Seite 148
RND(0)-Source.jpg
--H.T.W 13:06, 7. Sep. 2013 (CEST)
Anmerkung am Rande: Die Kommentierung bei obiger Quelle ist fehlerhaft. Es wird von CIA Timer A (Offset 04/05) und die Real Time Clock (Offset 08/09) verwendet - nicht wie fälschlicherweise beschrieben Timer B (der Offset 06/07 hat). ;) --JohannKlasek 09:53, 9. Sep. 2013 (CEST)
Also die inklusive und die exklusive Angabe verwirrt mich nun leider etwas. D.h. die 0 könnte vorkommen, während die 1 ausgeschlossen ist? --Jodigi 18:18, 8. Sep. 2013 (CEST)
Ja. Im englischen Wiki gibt's auf der Diskussionsseite auch ein Testprogramm dazu zu sehen. -- 1570 20:34, 8. Sep. 2013 (CEST)
Wenn wir es nun richtig ausdiskutiert haben, könnte der englische Artikel weiter ausgebaut werden. --Jodigi 02:21, 9. Sep. 2013 (CEST)
Tatsächlich ist bei RND(0) auch 0 möglich, denn im Fall TimerA und TOD10THS/TODSEC im CIA1 ist 0 werden alle Mantissenbits 0. Die bei RND am Ende aufgerufene Normalisierung des Float-Wertes im FAC ([1]) erkennt diese die leere Mantisse und macht daraus einen echten "0"-er, der eine extra Darstellung besitzt (nämlich Exponent in der codierten Form = 0, Mantisse egal, das entspräche dem Exponent 2^(-128) ).
Eine saubere "1" kann faktisch nicht erreicht werden, da dieser Wert (im FAC)
+----------+-----------------+-----------------+----------------+---------------+
| Exponent | höherwertige Bits <-------- Mantisse ---------> niederwertige Bits | 
+--------- +-----------------+-----------------+----------------+---------------+
| E ($61)  |    M1 ($62)     |     M2 ($63)    |     M3 ($64)   |    M4 ($65)   |
+--------- +-----------------+-----------------+----------------+---------------+
|  $81     |      00         |       00        |      00        |      00       | = 1.0
+--------- +-----------------+-----------------+----------------+---------------+
im Exponent $81 haben müsste, was aber nicht geht, weil RND den Exponent immer mit $80 vorgibt und die Normalisierung am Ende mit dieser Vorgabe nie auf $81 kommen kann.
Man kann nur einen Pseudo 1er erreichen, der aber nur als 1 (via PRINT) dargestellt wird, aber in Wirklichkeit nur sehr nahe bei 1 liegt. Erreichen kann man das - soweit meine Experimente gezeigt haben, es gelang mir nicht anders - durch einen speziellen Wert der in der Umkehrung RND-Funktion 1 ergeben würde. Diesen Wert kann man aber durch eine Konstantendarstellung nicht erreichen. Via POKE oder mit einem Monitor könnte man in einer Fließkommavariablen einen Wert festlegen, der als negativer Wert nur mit einer fixen Transformation als Startwert für eine Zufallszahlreihe dient (die Transformation vertauscht M1+M4 und M2+M3, siehe $E0D3).
+----------+-----------------+-----------------+----------------+---------------+
| Exponent | höherwertige Bits <-------- Mantisse ---------> niederwertige Bits | 
+--------- +-----------------+-----------------+----------------+---------------+
|  $80     |      $FD        |      $FF        |      $FF       |      $FF      | = -0.9921875 Ausgangswert
+--------- +-----------------+-----------------+----------------+---------------+ nach Transformation ...
|  $80     |      $FF        |      $FF        |      $FF       |      $FD      | = 1 (gerundet)
+--------- +-----------------+-----------------+----------------+---------------+
Mit einer simplen Zuweisung von -0.9921875 gelingt es nicht, das gesuchte Bitmuster in der Mantisse zu erzeugen (auch mit weiteren Kommastellen und der impliziten Rundung gelang es mir nicht). Wenn also die Variable obigen Wert enthält, dann zeigt
 PRINT A, PRINT RND(A)
nämlich:
 -0.9921875   1
Das es sich um keinen echten 1er handelt, sieht man, wenn durch Multiplikation die gerundeten Mantissenbits in den signifikanten Bereich "verschoben" werden:
 PRINT RND(A)*1E12
liefert dann doch nur 9.99999999E+11 - bei einer echten 1 wäre es bei 1E12 geblieben.
Testprogramm gesamt:
10 A=0
20 VP=PEEK(45)+PEEK(46)+2: REM ADRESSE DES WERTES DER VARIABLE "A"
30 FOR V=VP TO VP+4:READ F: POKE V,F: NEXT
40 DATA 128,253,255,255,255: REM FLOATWERT
50 PRINT A; RND(A); RND(A)*1E12: REM 1 bei RND!
Meines Erachtens - sofern nicht eine Lücke in meiner Argumentation steckt - hinreichend, dass die Existenz von 0 und Nichtexistenz von 1 als Ergebnis von RND bewiesen sein dürfte.
Soweit unser kleiner Ausflug in das Reich der Fließkommazahlen. ;)
--JohannKlasek 00:03, 10. Sep. 2013 (CEST)
Ha, da ist aber jemand weit abgetaucht. Hat aber mit RND eigentlich wenig zu tun, oder? Geht ja eher um Rundungsfehler bei der Darstellung von Fließkommazahlen bei PRINT. Ein "IF RND(A)=1" springt da ja nicht drauf an. - Johann, hast Du nicht Lust, mal was Genaueres zu Fließkommazahlen zu schreiben? Hier im Wiki habe ich keinen ausführlichen Artikel dazu gefunden (nur Redlinks, am besten noch in der Form [[Fließkomma|Gleitpunktzahl]], ganz großes Kino...) und weiß selbst auch nicht so viel darüber. Was den RND-Artikel angeht, fehlt denke ich eigentlich nur der Geschwindigkeitsvergleich und der genaue Algorithmus. GoDot hatte dazu schon was geschrieben, aber da fehlt noch das, was ab $E0D3 passiert (anscheinend Byte-Shuffling der Mantisse...? Und das ab $E0E3 ist eben das feste Setzen des Exponenten und nachfolgende Normalisierung (was heißt "Normalisierung"?)...? Hier steht unter "Perform RND" auch noch brauchbare Erklärung.). -- 1570 16:39, 10. Sep. 2013 (CEST)
$e0d3 wird aufgerufen, wenn das Argument der Funktion negativ ist (also RND(-1) oder RND(-TI)). Normalisierung kuckst du hier. --GoDot 17:01, 10. Sep. 2013 (CEST)
Noch was: Auf .com gibt es tatsächlich schon einen halbwegs brauchbaren Artikel zu Floats: Floating_point_arithmetic
$E0D3 kommt auch bei Argumenten >0 dran, der Code nach E09C: BNE $E0BE läuft dahin durch. Wäre ja sonst komisch, denn x' = x * c1 + c2 ist ja wirklich nicht so arg zufällig. Nur RND(0) benutzt kein Shuffling. (nebenbei, für meine Referenz: In Mapping the C64 steht einiges zu Fließkommazahlen unter "Floating Point Accumulator #1") -- 1570 17:33, 10. Sep. 2013 (CEST)
Stimmt genau - brauch ich's also nicht mehr zu erklären. :) --JohannKlasek 19:24, 10. Sep. 2013 (CEST)
Ja, diese Recherchen passierten in der Tat (jetzt kann ich's ja veraten ;), um den dt. Fließkomma-Artikel (=Fließkommazahl, analog zu Integer, Ganzzahl) vorzubereiten. Ich sammle schon seit geraumer Zeit. Ich werd' vom engl. Artikel sicherlich auch etwas zur Orientierung mitnehmen. Der zum englischen Artikel korrespondierende Artikel wäre aber mMn ein separater Artikel ("Fließkommazahlarithmetik"), wo man dann fein säuberlich die ganzen Routinen wie in der engl. Vorlage präsentieren kann.
Danke für die Links. :) --JohannKlasek 19:24, 10. Sep. 2013 (CEST)