3

In AppleSoft, RND with a negative number performs a seed on the generator with that number. So, as I understand it X=RND(-1) is the equivalent of RANDOMIZE 1:X=RND(1):

If aexpr is positive, returns a random number from 0 to 0.999.... If aexpr is zero, repeats the last result. If aexpr is negative, reseeds the generator.

That middle bit is a surprise to me, and seems quite odd from a portability perspective considering how many programs used 0 as a dummy value. I assume Apple users simply learned to change this to some other value, like 1.

The C64 quick reference states:

RND(exprnm)—Returns a random number between and 1 if exprnm is positive. If exprnm is zero, returns, a "randomized" random number. If exprnm is negative, returns a preset random number.

This is similar to the AppleSoft version (as one would expect) except for the zero case, and that bit about the "'randomized' random number" could mean one of several things. So I looked in the C64 user guide page 126 and get this:

RND(X) returns a random number in the range 0-1. The first random number should be generated by the formula RND(-TI) to start things off differently every time. After this, X should be a 1 or any positive number. If X is zero, the result will be the same random number as the last one.

That seems quite at odds with the version in the quick reference. So then I checked the C64 wiki page on RND:

  • By using RND(<positive number>) gives a different random number each time from a predetermined sequence (the sequence number is stored internally).
  • […] typical use is to call RND(<negative number>) once and then repeatedly call RND(<positive number>).

So, my questions for those more deeply familiar with the Commodore systems:

  1. is the issue with RND(0) simply because the C64, specifically, does not automatically start the appropriate timer at startup? The wiki suggests that other CBM platforms do, and that the statement in the Quick Reference is thus accurate for those machines (and likely just copied).

  2. are all positive parameters otherwise equivalent? Will RND(10) and RND(20) perform the same internal operation? The wiki can be read to suggest these are different sequences, but I find that difficult to believe.

4
  • 1
    It would have been nice if you included a precise citation of what you’re quoting. I presume the first quote is from <calormen.com/jsbasic/reference.html>? Commented Feb 12 at 18:05
  • I note that BBC BASIC has exactly the same odd behaviour for RND(0) as you describe. In full: RND(-X) generates a seed from X and returns -X, RND(1) bumps the seed then uses it to generate and return a float in the range [0,1), RND(X) bumps the seed and uses it to generate and return an integer in the range [0,X) and RND(0) "repeats the last random number given by RND(1)", but actually just uses the unchanged seed to generate a float in the range [0,1) rather than having remembered the previous RND(0). So in P.RND(1000):P.RND(0), the second number will be approximately 1/1000th of the first.
    – pndc
    Commented Feb 12 at 18:50
  • "seed from X and returns -X" - oh, I had understood it to seed and then return "a random number", not X. I don't have a beeb emulator, I guess I'll have to get one. Commented Feb 12 at 19:39
  • @MauryMarkowitz - the behaviour of BBC BASIC is documented in the manual: RND (scroll down). Also, a BBC Emulator, you said? Here's Owlet in your browser
    – scruss
    Commented Feb 14 at 1:40

1 Answer 1

2

I looked at the source code, and this is what I can say about random number generation in Microsoft 6502 BASIC:

  • If the argument to RND is negative, its significand bits are directly used as the seed.
  • If the argument is positive, it is discarded; the last generated value is multiplied by a constant A, then another constant B is added to it. The significand bits of the result are used as the seed. There is a fallback “remembered” value C used if no random numbers have been generated before. The constants A, B and C are fixed for all versions of Microsoft 6502 BASIC, up to configured floating-point width.0
  • On Commodore platforms only, if an argument of 0 is given, the seed bits are apparently obtained from timer registers; on the PET, those are VIA chip registers at addresses $E844…$E845 and $E848…$E849. Microsoft BASIC for other platforms treats a zero argument like positive ones.

After the seed is selected, the generation algorithm is as follows: the bytes of the seed are reversed, then used as the mantissa of the result. The sign is forced to be positive and the exponent to be zero. The value generated is normalized, remembered for future generation requests and returned to the caller.

The assembly code from that source seems to match Commodore PET BASIC only; the C64 version differs in that while it similarly accesses registers $DC04…$DC05 and $DC08…$DC09 of the CIA chip, the meanings of those addresses are changed. The latter pair of addresses no longer refers to a real-time timer counter, but instead to a wall-clock time seconds and tenths of seconds counter.

It is unclear whether this is an oversight or deliberate choice. I can, however, confirm from experimentation in VICE that on the PET, both timers run, while on the C64, the second timer indeed stands still by default, so the switch to RTC registers may have been intended to compensate, and not merely the result of a coincidence that the RTC registers are at the same offset from the first timer register on the C64 that the second timer register was on earlier Commodore platforms.


0 As far as I was able to determine, A = 11 879 546.406 25 × 20, B = 11 055 430.125 × 2−52 and C = 13 616 978.347 656 25 × 2−24. These are the values for standard 40-bit builds; the values for 32-bit builds are obtained by discarding the fractional part from each left factor. Interestingly, the values of A and B in 40-bit builds result from a mistake: the source code specifies only four bytes for the floating-point constants regardless of the chosen width, so in 40-bit builds the values overlap each other, and the following JSR instruction of the RND subroutine. C64 BASIC corrects this by adding a padding zero byte, and therefore uses the exact same values as 32-bit builds for A and B, despite otherwise using 40-bit floats.

1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .