~ chicken-core (chicken-5) 6a3a8253525816e876ca48398cbb2b3e8cf3dd11


commit 6a3a8253525816e876ca48398cbb2b3e8cf3dd11
Author:     Kooda <kooda@upyum.com>
AuthorDate: Sat Nov 4 13:10:21 2017 +0100
Commit:     Kooda <kooda@upyum.com>
CommitDate: Sat Nov 4 13:12:20 2017 +0100

    Add pseudo-random-real to the chicken.random module.
    
    Based on Taylor R. Campbell algorithm explained on
    https://mumble.net/~campbell/2014/04/28/uniform-random-float

diff --git a/chicken.h b/chicken.h
index 357928b9..b0a7de30 100644
--- a/chicken.h
+++ b/chicken.h
@@ -2095,6 +2095,7 @@ C_fctexport void *C_lookup_procedure_ptr(C_char *id);
 
 C_fctexport C_word C_random_fixnum(C_word n) C_regparm;
 C_fctexport C_word C_fcall C_s_a_u_i_random_int(C_word **ptr, C_word n, C_word rn) C_regparm;
+C_fctexport C_word C_fcall C_a_i_random_real(C_word **ptr, C_word n) C_regparm;
 C_fctexport C_word C_random_bytes(C_word buf, C_word size);
 C_fctexport C_word C_set_random_seed(C_word buf, C_word n);
 
diff --git a/extras.scm b/extras.scm
index 10e1bf0c..0eaacd32 100644
--- a/extras.scm
+++ b/extras.scm
@@ -644,7 +644,7 @@
 ;;; Random numbers:
 
 (module chicken.random
-  (set-pseudo-random-seed! pseudo-random-integer random-bytes)
+  (set-pseudo-random-seed! pseudo-random-integer pseudo-random-real random-bytes)
 
 (import scheme chicken chicken.time chicken.io foreign)
 
@@ -668,6 +668,9 @@
         (else
           (##core#inline_allocate ("C_s_a_u_i_random_int" 2) n))))
 
+(define (pseudo-random-real)
+  (##core#inline_allocate ("C_a_i_random_real" 2)))
+
 (define random-bytes
   (let ((nstate (foreign-value "C_RANDOM_STATE_SIZE" unsigned-int)))
     (lambda (#!optional buf size)
diff --git a/runtime.c b/runtime.c
index 9d312478..5dc17466 100644
--- a/runtime.c
+++ b/runtime.c
@@ -12676,6 +12676,38 @@ C_s_a_u_i_random_int(C_word **ptr, C_word n, C_word rn)
   return C_bignum_simplify(result);
 }
 
+/*
+ * C_a_i_random_real: Generate a stream of bits uniformly at random and
+ * interpret it as the fractional part of the binary expansion of a
+ * number in [0, 1], 0.00001010011111010100...; then round it.
+ * More information on https://mumble.net/~campbell/2014/04/28/uniform-random-float
+ */
+#define random64() ((((C_u64) random_word()) << 32) | ((C_u64) random_word()))
+#define	clz64	__builtin_clzll		/* XXX GCCism */
+C_regparm C_word C_fcall
+C_a_i_random_real(C_word **ptr, C_word n) {
+  int exponent = -64;
+  uint64_t significand;
+  unsigned shift;
+
+  while (C_unlikely((significand = random64()) == 0)) {
+    exponent -= 64;
+    if (C_unlikely(exponent < -1074))
+      return 0;
+  }
+
+  shift = clz64(significand);
+  if (shift != 0) {
+    exponent -= shift;
+    significand <<= shift;
+    significand |= (random64() >> (64 - shift));
+  }
+
+  significand |= 1;
+  return C_flonum(ptr, ldexp((double)significand, exponent));
+}
+#undef random64
+
 
 C_word C_set_random_seed(C_word buf, C_word n)
 {
Trap