Hi, all
Frank Heckenbach wrote:
Toby Ewing wrote:
Frank Heckenbach wrote:
I've briefly looked at your code, and it seems there are some off-by-one errors, e.g.:
for kk := mtN-mtM to mtN-1 do begin
(where the C version says `<N-1'). I think there are more.
There are indeed several such differences, but they're not errors as such. I used the Pascal [1..n] convention, rather than staying with the C [0..n-1] convention.
Then these lines seem to be wrong:
type MTary = array[0..mtN] of MTcard; magary = array[0..1] of MTcard;
for kk := 0 to mtN-mtM-1 do begin
Well, `[0 .. mtN]' is one too large for C's `[mtN]' in any case. Maybe the extra element is just declared and computed but never used, so the results are the same, but it's still a bit strange.
I mis-spoke above. The code uses mt[mtN] during initialization, but the generated random numbers are stored in mt[0..mtN-1].
See the info file GPCS that comes with GPC (long) and/or just follow the style and interfaces in random.pas. I'd also appreciate if you keep the identifier names as far as possible so it's easy to see what's actually changed.
thanks for the pointer.
If you want to add more interfaces, I'd agree if they're nontrivial enough -- but in contrast e.g. `randrEx1 (range)' is equivalent to (and in fact does the same as) `range * Random' when `Random' calls `Default_RandReal' internally, so I don't think it justifies another RTS function.
I agree in the specific case of randrEx1 - it is trivial. But it was included to fill out the complete suite of random doubles in: [0..1) [0..r) [0..1] [0..r] (0..1] (0..r] (0..1) (0..r) which may look like overkill, but there are times for each.
I also doubt whether the bit-fiddling in `randrInt' is really better than our `Default_RandInt' code (which looks more complicated, but only because it also provides a 64 bit range -- though it may be worthwhile to add a "fast path" for MaxValue <= RandomRange there). Assuming that the random operation is the most costly part, using `mod' seems better than `and'ing out bits -- how much, of course, depends on the number, e.g. for 0 .. 32, the `and' version will require almost two calls on average (64/33) whereas the `mod' version takes just slightly more than one (1 + 4/(2^32-4)).
Can't say offhand. I'll look into this one, and get back to you.
At a quick glance, also `randNorm' seems problematic since if `bsecond' is set, it ignores its arguments.
This can look confusing at first glance, but it's actually a rather nice algorithm. The Box-Muller algorithm (old and venerable) takes as arguments 2 uniform random deviates (random numbers in [0..1]) and returns 2 uncorrelated numbers drawn from a normal distribution. If you generate 2 uniform random deviates each time you ask for one normally distributed number, then you're throwing away a lot of effort. The function as I wrote it has two static variables (1 boolean, 1 real). The boolean is initialized as false. When the boolean is false, the function generates 2 random numbers, stores 1 (statically), sets the boolean to true, and returns the other random number. If the boolean is true (i.e. next time), the function sets the boolean to false and returns the stored number. It's definitely faster than the C++ method I started with, which only generates 1 random normal for everly 2 uniform random deviates.
The code that I started with, the C++ abyss from which I rescued some civilized Pascal, claims to be faster than what I wrote. It uses the C and C++ directives "inline" and "register", and some arcane C constructions that are better avoided in clear code.
Yes, GPC provides them as attributes, but with `-O3' for `inline' and `-O' for `register', it also does it automatically, so they're recommended only in very special cases.
I found that the speedup resulting from going from -O2 to -O3 was quite significant. Presumably that is due to the inlining. I'm not familiar with the -O attribute: can you please say more about how and where to use it?
thanks, Toby