mirror of https://github.com/GNOME/gimp.git
Bug 690544 - Script-fu (rand 4294967295) hangs on machines with 64-bit longs
This commit is contained in:
parent
b3c6163450
commit
139801f222
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
;The random number generator routines below have been slightly reformatted.
|
;The random number generator routines below have been slightly reformatted.
|
||||||
;A couple of define blocks which are not needed have been commented out.
|
;A couple of define blocks which are not needed have been commented out.
|
||||||
|
;It has also been extended to enable it to generate numbers with exactly 31
|
||||||
|
;bits or more.
|
||||||
;The original file was called rand2.scm and can be found in:
|
;The original file was called rand2.scm and can be found in:
|
||||||
;http://www-2.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/scheme/code/math/random/
|
;http://www-2.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/scheme/code/math/random/
|
||||||
|
|
||||||
|
@ -65,13 +67,14 @@
|
||||||
; Bust (abcde) 0.3024 0.3058
|
; Bust (abcde) 0.3024 0.3058
|
||||||
|
|
||||||
(define (random n)
|
(define (random n)
|
||||||
|
(define (internal-random n)
|
||||||
(let* (
|
(let* (
|
||||||
(n (inexact->exact (truncate n)))
|
(n (inexact->exact (truncate n)))
|
||||||
(M 2147483647)
|
(M 2147483647)
|
||||||
(slop (modulo M n))
|
(slop (modulo M (abs n)))
|
||||||
)
|
)
|
||||||
(let loop ((r (msrg-rand)))
|
(let loop ((r (msrg-rand)))
|
||||||
(if (> r slop)
|
(if (>= r slop)
|
||||||
(modulo r n)
|
(modulo r n)
|
||||||
(loop (msrg-rand))
|
(loop (msrg-rand))
|
||||||
)
|
)
|
||||||
|
@ -79,6 +82,88 @@
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
; Negative numbers have a bigger range in twos complement platforms
|
||||||
|
; (nearly all platforms out there) than positive ones, so we deal with
|
||||||
|
; the numbers in negative form.
|
||||||
|
(if (> n 0)
|
||||||
|
(+ n (random (- n)))
|
||||||
|
|
||||||
|
(if (>= n -2147483647)
|
||||||
|
(internal-random n)
|
||||||
|
|
||||||
|
; 31-or-more-bits number requested - needs multiple extractions
|
||||||
|
; because we don't generate enough random bits.
|
||||||
|
(if (>= n -1152921504606846975)
|
||||||
|
; Up to 2^60-1, two extractions are enough
|
||||||
|
(let ((q (- (quotient (+ n 1) 1073741824) 1))) ; q=floor(n/2^30)
|
||||||
|
(let loop ()
|
||||||
|
(let ((big (+ (* (internal-random q) 1073741824)
|
||||||
|
(internal-random -1073741824)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
(if (> big n)
|
||||||
|
big
|
||||||
|
(loop)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
; From 2^60 up, we do three extractions.
|
||||||
|
; The code is better understood if seen as generating three
|
||||||
|
; digits in base 2^30. q is the maximum value the first digit
|
||||||
|
; can take. The other digits can take the full range.
|
||||||
|
;
|
||||||
|
; The strategy is to generate a random number digit by digit.
|
||||||
|
; Here's an example in base 10. Say the input n is 348
|
||||||
|
; (thus requesting a number between 0 and 347). Then the algorithm
|
||||||
|
; first calls (internal-random 4) to get a digit betweeen 0 and 3,
|
||||||
|
; then (internal-random 10) twice to get two more digits between
|
||||||
|
; 0 and 9. Say the result is 366: since it is greater than 347,
|
||||||
|
; it's discarded and the process restarted. When the result is
|
||||||
|
; <= 347, that's the returned value. The probability of it being
|
||||||
|
; greater than the max is always strictly less than 1/2.
|
||||||
|
;
|
||||||
|
; This is the same idea but in base 2^30 (1073741824). The
|
||||||
|
; first digit's weight is (2^30)^2 = 1152921504606846976,
|
||||||
|
; similarly to how in our base 10 example, the first digit's
|
||||||
|
; weight is 10^2 = 100. In the base 10 example we first divide
|
||||||
|
; the target number 348 by 100, taking the ceiling, to get 4.
|
||||||
|
; Here we divide by (2^30)^2 instead, taking the ceiling too.
|
||||||
|
;
|
||||||
|
; The math is a bit obscured by the fact that we generate
|
||||||
|
; the digits as negative, so that the result is negative as
|
||||||
|
; well, but it's really the same thing. Changing the sign of
|
||||||
|
; every digit just changes the sign of the result.
|
||||||
|
;
|
||||||
|
; This method works for n up to (2^30)^2*(2^31-1) which is
|
||||||
|
; 2475880077417839045191401472 (slightly under 91 bits). That
|
||||||
|
; covers the 64-bit range comfortably, and some more. If larger
|
||||||
|
; numbers are needed, they'll have to be composed with a
|
||||||
|
; user-defined procedure.
|
||||||
|
|
||||||
|
(if (>= n -2475880077417839045191401472)
|
||||||
|
(let ((q (- (quotient (+ n 1) 1152921504606846976) 1))) ; q=floor(n/2^60)
|
||||||
|
(let loop ()
|
||||||
|
(let ((big (+ (* (internal-random q) 1152921504606846976)
|
||||||
|
(* (internal-random -1073741824) 1073741824)
|
||||||
|
(internal-random -1073741824)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
(if (> big n)
|
||||||
|
big
|
||||||
|
(loop)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(error "requested (random n) range too large")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
;(define (rngtest)
|
;(define (rngtest)
|
||||||
; (display "implementation ")
|
; (display "implementation ")
|
||||||
; (srand 1)
|
; (srand 1)
|
||||||
|
|
Loading…
Reference in New Issue