]> Cypherpunks repositories - gostls13.git/commitdiff
math/rand: restore Go 1.2 value stream for Float32, Float64
authorRuss Cox <rsc@golang.org>
Mon, 19 May 2014 16:30:25 +0000 (12:30 -0400)
committerRuss Cox <rsc@golang.org>
Mon, 19 May 2014 16:30:25 +0000 (12:30 -0400)
CL 22730043 fixed a bug in these functions: they could
return 1.0 despite documentation saying otherwise.
But the fix changed the values returned in the non-buggy case too,
which might invalidate programs depending on a particular
stream when using rand.Seed(0) or when passing their own
Source to rand.New.

The example test says:
        // These tests serve as an example but also make sure we don't change
        // the output of the random number generator when given a fixed seed.
so I think there is some justification for thinking we have
promised not to change the values. In any case, there's no point in
changing the values gratuitously: we can easily fix this bug without
changing the values, and so we should.

That CL just changed the test values too, which defeats the
stated purpose, but it was just a comment.
Add an explicit regression test, which might be
a clearer signal next time that we don't want to change
the values.

Fixes #6721. (again)
Fixes #8013.

LGTM=r
R=iant, r
CC=golang-codereviews
https://golang.org/cl/95460049

src/pkg/math/rand/example_test.go
src/pkg/math/rand/rand.go
src/pkg/math/rand/regress_test.go [new file with mode: 0644]

index b93a371a043d79936062fd658777ac6d8745443b..f429914531a98ed59fb13ffa01bcf81f28db42f3 100644 (file)
@@ -83,8 +83,8 @@ func Example_rand() {
        // Perm generates a random permutation of the numbers [0, n).
        show("Perm", r.Perm(5), r.Perm(5), r.Perm(5))
        // Output:
-       // Float32     0.73793465          0.38461488          0.9940225
-       // Float64     0.6919607852308565  0.29140004584133117 0.2262092163027547
+       // Float32     0.2635776           0.6358173           0.6718283
+       // Float64     0.628605430454327   0.4504798828572669  0.9562755949377957
        // ExpFloat64  0.3362240648200941  1.4256072328483647  0.24354758816173044
        // NormFloat64 0.17233959114940064 1.577014951434847   0.04259129641113857
        // Int31       1501292890          1486668269          182840835
index 0c91f88184eee2fdddb8657eec7bd5e11b596e97..3ffb5c4e5c64d2062e448e028146028aed940693 100644 (file)
@@ -101,10 +101,46 @@ func (r *Rand) Intn(n int) int {
 }
 
 // Float64 returns, as a float64, a pseudo-random number in [0.0,1.0).
-func (r *Rand) Float64() float64 { return float64(r.Int63n(1<<53)) / (1 << 53) }
+func (r *Rand) Float64() float64 {
+       // A clearer, simpler implementation would be:
+       //      return float64(r.Int63n(1<<53)) / (1<<53)
+       // However, Go 1 shipped with
+       //      return float64(r.Int63()) / (1 << 63)
+       // and we want to preserve that value stream.
+       //
+       // There is one bug in the value stream: r.Int63() may be so close
+       // to 1<<63 that the division rounds up to 1.0, and we've guaranteed
+       // that the result is always less than 1.0. To fix that, we treat the
+       // range as cyclic and map 1 back to 0. This is justified by observing
+       // that while some of the values rounded down to 0, nothing was
+       // rounding up to 0, so 0 was underrepresented in the results.
+       // Mapping 1 back to zero restores some balance.
+       // (The balance is not perfect because the implementation
+       // returns denormalized numbers for very small r.Int63(),
+       // and those steal from what would normally be 0 results.)
+       // The remapping only happens 1/2⁵³ of the time, so most clients
+       // will not observe it anyway.
+       f := float64(r.Int63()) / (1 << 63)
+       if f == 1 {
+               f = 0
+       }
+       return f
+}
 
 // Float32 returns, as a float32, a pseudo-random number in [0.0,1.0).
-func (r *Rand) Float32() float32 { return float32(r.Int31n(1<<24)) / (1 << 24) }
+func (r *Rand) Float32() float32 {
+       // Same rationale as in Float64: we want to preserve the Go 1 value
+       // stream except we want to fix it not to return 1.0
+       // There is a double rounding going on here, but the argument for
+       // mapping 1 to 0 still applies: 0 was underrepresented before,
+       // so mapping 1 to 0 doesn't cause too many 0s.
+       // This only happens 1/2²⁴ of the time (plus the 1/2⁵³ of the time in Float64).
+       f := float32(r.Float64())
+       if f == 1 {
+               f = 0
+       }
+       return f
+}
 
 // Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n).
 func (r *Rand) Perm(n int) []int {
diff --git a/src/pkg/math/rand/regress_test.go b/src/pkg/math/rand/regress_test.go
new file mode 100644 (file)
index 0000000..2b012af
--- /dev/null
@@ -0,0 +1,355 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that random number sequences generated by a specific seed
+// do not change from version to version.
+//
+// Do NOT make changes to the golden outputs. If bugs need to be fixed
+// in the underlying code, find ways to fix them that do not affect the
+// outputs.
+
+package rand_test
+
+import (
+       "flag"
+       "fmt"
+       . "math/rand"
+       "reflect"
+       "testing"
+)
+
+var printgolden = flag.Bool("printgolden", false, "print golden results for regression test")
+
+func TestRegress(t *testing.T) {
+       var int32s = []int32{1, 10, 32, 1 << 20, 1<<20 + 1, 1000000000, 1 << 30, 1<<31 - 2, 1<<31 - 1}
+       var int64s = []int64{1, 10, 32, 1 << 20, 1<<20 + 1, 1000000000, 1 << 30, 1<<31 - 2, 1<<31 - 1, 1000000000000000000, 1 << 60, 1<<63 - 2, 1<<63 - 1}
+       var permSizes = []int{0, 1, 5, 8, 9, 10, 16}
+       r := New(NewSource(0))
+
+       rv := reflect.ValueOf(r)
+       n := rv.NumMethod()
+       p := 0
+       if *printgolden {
+               fmt.Printf("var regressGolden = []interface{}{\n")
+       }
+       for i := 0; i < n; i++ {
+               m := rv.Type().Method(i)
+               mv := rv.Method(i)
+               mt := mv.Type()
+               if mt.NumOut() == 0 {
+                       continue
+               }
+               if mt.NumOut() != 1 {
+                       t.Fatalf("unexpected result count for r.%s", m.Name)
+               }
+               r.Seed(0)
+               for repeat := 0; repeat < 20; repeat++ {
+                       var args []reflect.Value
+                       var argstr string
+                       if mt.NumIn() == 1 {
+                               var x interface{}
+                               switch mt.In(0).Kind() {
+                               default:
+                                       t.Fatalf("unexpected argument type for r.%s", m.Name)
+
+                               case reflect.Int:
+                                       if m.Name == "Perm" {
+                                               x = permSizes[repeat%len(permSizes)]
+                                               break
+                                       }
+                                       big := int64s[repeat%len(int64s)]
+                                       if int64(int(big)) != big {
+                                               r.Int63n(big) // what would happen on 64-bit machine, to keep stream in sync
+                                               if *printgolden {
+                                                       fmt.Printf("\tskipped, // must run printgolden on 64-bit machine\n")
+                                               }
+                                               p++
+                                               continue
+                                       }
+                                       x = int(big)
+
+                               case reflect.Int32:
+                                       x = int32s[repeat%len(int32s)]
+
+                               case reflect.Int64:
+                                       x = int64s[repeat%len(int64s)]
+                               }
+                               argstr = fmt.Sprint(x)
+                               args = append(args, reflect.ValueOf(x))
+                       }
+                       out := mv.Call(args)[0].Interface()
+                       if m.Name == "Int" || m.Name == "Intn" {
+                               out = int64(out.(int))
+                       }
+                       if *printgolden {
+                               var val string
+                               big := int64(1 << 60)
+                               if int64(int(big)) != big && (m.Name == "Int" || m.Name == "Intn") {
+                                       // 32-bit machine cannot print 64-bit results
+                                       val = "truncated"
+                               } else if reflect.TypeOf(out).Kind() == reflect.Slice {
+                                       val = fmt.Sprintf("%#v", out)
+                               } else {
+                                       val = fmt.Sprintf("%T(%v)", out, out)
+                               }
+                               fmt.Printf("\t%s, // %s(%s)\n", val, m.Name, argstr)
+                       } else {
+                               want := regressGolden[p]
+                               if m.Name == "Int" {
+                                       want = int64(int(uint(want.(int64)) << 1 >> 1))
+                               }
+                               if !reflect.DeepEqual(out, want) {
+                                       t.Errorf("r.%s(%s) = %v, want %v", m.Name, argstr, out, want)
+                               }
+                       }
+                       p++
+               }
+       }
+       if *printgolden {
+               fmt.Printf("}\n")
+       }
+}
+
+var regressGolden = []interface{}{
+       float64(4.668112973579268),    // ExpFloat64()
+       float64(0.1601593871172866),   // ExpFloat64()
+       float64(3.0465834105636),      // ExpFloat64()
+       float64(0.06385839451671879),  // ExpFloat64()
+       float64(1.8578917487258961),   // ExpFloat64()
+       float64(0.784676123472182),    // ExpFloat64()
+       float64(0.11225477361256932),  // ExpFloat64()
+       float64(0.20173283329802255),  // ExpFloat64()
+       float64(0.3468619496201105),   // ExpFloat64()
+       float64(0.35601103454384536),  // ExpFloat64()
+       float64(0.888376329507869),    // ExpFloat64()
+       float64(1.4081362450365698),   // ExpFloat64()
+       float64(1.0077753823151994),   // ExpFloat64()
+       float64(0.23594100766227588),  // ExpFloat64()
+       float64(2.777245612300007),    // ExpFloat64()
+       float64(0.5202997830662377),   // ExpFloat64()
+       float64(1.2842705247770294),   // ExpFloat64()
+       float64(0.030307408362776206), // ExpFloat64()
+       float64(2.204156824853721),    // ExpFloat64()
+       float64(2.09891923895058),     // ExpFloat64()
+       float32(0.94519615),           // Float32()
+       float32(0.24496509),           // Float32()
+       float32(0.65595627),           // Float32()
+       float32(0.05434384),           // Float32()
+       float32(0.3675872),            // Float32()
+       float32(0.28948045),           // Float32()
+       float32(0.1924386),            // Float32()
+       float32(0.65533215),           // Float32()
+       float32(0.8971697),            // Float32()
+       float32(0.16735445),           // Float32()
+       float32(0.28858566),           // Float32()
+       float32(0.9026048),            // Float32()
+       float32(0.84978026),           // Float32()
+       float32(0.2730468),            // Float32()
+       float32(0.6090802),            // Float32()
+       float32(0.253656),             // Float32()
+       float32(0.7746542),            // Float32()
+       float32(0.017480763),          // Float32()
+       float32(0.78707397),           // Float32()
+       float32(0.7993937),            // Float32()
+       float64(0.9451961492941164),   // Float64()
+       float64(0.24496508529377975),  // Float64()
+       float64(0.6559562651954052),   // Float64()
+       float64(0.05434383959970039),  // Float64()
+       float64(0.36758720663245853),  // Float64()
+       float64(0.2894804331565928),   // Float64()
+       float64(0.19243860967493215),  // Float64()
+       float64(0.6553321508148324),   // Float64()
+       float64(0.897169713149801),    // Float64()
+       float64(0.16735444255905835),  // Float64()
+       float64(0.2885856518054551),   // Float64()
+       float64(0.9026048462705047),   // Float64()
+       float64(0.8497802817628735),   // Float64()
+       float64(0.2730468047134829),   // Float64()
+       float64(0.6090801919903561),   // Float64()
+       float64(0.25365600644283687),  // Float64()
+       float64(0.7746542391859803),   // Float64()
+       float64(0.017480762156647272), // Float64()
+       float64(0.7870739563039942),   // Float64()
+       float64(0.7993936979594545),   // Float64()
+       int64(8717895732742165505),    // Int()
+       int64(2259404117704393152),    // Int()
+       int64(6050128673802995827),    // Int()
+       int64(501233450539197794),     // Int()
+       int64(3390393562759376202),    // Int()
+       int64(2669985732393126063),    // Int()
+       int64(1774932891286980153),    // Int()
+       int64(6044372234677422456),    // Int()
+       int64(8274930044578894929),    // Int()
+       int64(1543572285742637646),    // Int()
+       int64(2661732831099943416),    // Int()
+       int64(8325060299420976708),    // Int()
+       int64(7837839688282259259),    // Int()
+       int64(2518412263346885298),    // Int()
+       int64(5617773211005988520),    // Int()
+       int64(2339563716805116249),    // Int()
+       int64(7144924247938981575),    // Int()
+       int64(161231572858529631),     // Int()
+       int64(7259475919510918339),    // Int()
+       int64(7373105480197164748),    // Int()
+       int32(2029793274),             // Int31()
+       int32(526058514),              // Int31()
+       int32(1408655353),             // Int31()
+       int32(116702506),              // Int31()
+       int32(789387515),              // Int31()
+       int32(621654496),              // Int31()
+       int32(413258767),              // Int31()
+       int32(1407315077),             // Int31()
+       int32(1926657288),             // Int31()
+       int32(359390928),              // Int31()
+       int32(619732968),              // Int31()
+       int32(1938329147),             // Int31()
+       int32(1824889259),             // Int31()
+       int32(586363548),              // Int31()
+       int32(1307989752),             // Int31()
+       int32(544722126),              // Int31()
+       int32(1663557311),             // Int31()
+       int32(37539650),               // Int31()
+       int32(1690228450),             // Int31()
+       int32(1716684894),             // Int31()
+       int32(0),                      // Int31n(1)
+       int32(4),                      // Int31n(10)
+       int32(25),                     // Int31n(32)
+       int32(310570),                 // Int31n(1048576)
+       int32(857611),                 // Int31n(1048577)
+       int32(621654496),              // Int31n(1000000000)
+       int32(413258767),              // Int31n(1073741824)
+       int32(1407315077),             // Int31n(2147483646)
+       int32(1926657288),             // Int31n(2147483647)
+       int32(0),                      // Int31n(1)
+       int32(8),                      // Int31n(10)
+       int32(27),                     // Int31n(32)
+       int32(367019),                 // Int31n(1048576)
+       int32(209005),                 // Int31n(1048577)
+       int32(307989752),              // Int31n(1000000000)
+       int32(544722126),              // Int31n(1073741824)
+       int32(1663557311),             // Int31n(2147483646)
+       int32(37539650),               // Int31n(2147483647)
+       int32(0),                      // Int31n(1)
+       int32(4),                      // Int31n(10)
+       int64(8717895732742165505),    // Int63()
+       int64(2259404117704393152),    // Int63()
+       int64(6050128673802995827),    // Int63()
+       int64(501233450539197794),     // Int63()
+       int64(3390393562759376202),    // Int63()
+       int64(2669985732393126063),    // Int63()
+       int64(1774932891286980153),    // Int63()
+       int64(6044372234677422456),    // Int63()
+       int64(8274930044578894929),    // Int63()
+       int64(1543572285742637646),    // Int63()
+       int64(2661732831099943416),    // Int63()
+       int64(8325060299420976708),    // Int63()
+       int64(7837839688282259259),    // Int63()
+       int64(2518412263346885298),    // Int63()
+       int64(5617773211005988520),    // Int63()
+       int64(2339563716805116249),    // Int63()
+       int64(7144924247938981575),    // Int63()
+       int64(161231572858529631),     // Int63()
+       int64(7259475919510918339),    // Int63()
+       int64(7373105480197164748),    // Int63()
+       int64(0),                      // Int63n(1)
+       int64(2),                      // Int63n(10)
+       int64(19),                     // Int63n(32)
+       int64(959842),                 // Int63n(1048576)
+       int64(688912),                 // Int63n(1048577)
+       int64(393126063),              // Int63n(1000000000)
+       int64(89212473),               // Int63n(1073741824)
+       int64(834026388),              // Int63n(2147483646)
+       int64(1577188963),             // Int63n(2147483647)
+       int64(543572285742637646),     // Int63n(1000000000000000000)
+       int64(355889821886249464),     // Int63n(1152921504606846976)
+       int64(8325060299420976708),    // Int63n(9223372036854775806)
+       int64(7837839688282259259),    // Int63n(9223372036854775807)
+       int64(0),                      // Int63n(1)
+       int64(0),                      // Int63n(10)
+       int64(25),                     // Int63n(32)
+       int64(679623),                 // Int63n(1048576)
+       int64(882178),                 // Int63n(1048577)
+       int64(510918339),              // Int63n(1000000000)
+       int64(782454476),              // Int63n(1073741824)
+       int64(0),                      // Intn(1)
+       int64(4),                      // Intn(10)
+       int64(25),                     // Intn(32)
+       int64(310570),                 // Intn(1048576)
+       int64(857611),                 // Intn(1048577)
+       int64(621654496),              // Intn(1000000000)
+       int64(413258767),              // Intn(1073741824)
+       int64(1407315077),             // Intn(2147483646)
+       int64(1926657288),             // Intn(2147483647)
+       int64(543572285742637646),     // Intn(1000000000000000000)
+       int64(355889821886249464),     // Intn(1152921504606846976)
+       int64(8325060299420976708),    // Intn(9223372036854775806)
+       int64(7837839688282259259),    // Intn(9223372036854775807)
+       int64(0),                      // Intn(1)
+       int64(2),                      // Intn(10)
+       int64(14),                     // Intn(32)
+       int64(515775),                 // Intn(1048576)
+       int64(839455),                 // Intn(1048577)
+       int64(690228450),              // Intn(1000000000)
+       int64(642943070),              // Intn(1073741824)
+       float64(-0.28158587086436215), // NormFloat64()
+       float64(0.570933095808067),    // NormFloat64()
+       float64(-1.6920196326157044),  // NormFloat64()
+       float64(0.1996229111693099),   // NormFloat64()
+       float64(1.9195199291234621),   // NormFloat64()
+       float64(0.8954838794918353),   // NormFloat64()
+       float64(0.41457072128813166),  // NormFloat64()
+       float64(-0.48700161491544713), // NormFloat64()
+       float64(-0.1684059662402393),  // NormFloat64()
+       float64(0.37056410998929545),  // NormFloat64()
+       float64(1.0156889027029008),   // NormFloat64()
+       float64(-0.5174422210625114),  // NormFloat64()
+       float64(-0.5565834214413804),  // NormFloat64()
+       float64(0.778320596648391),    // NormFloat64()
+       float64(-1.8970718197702225),  // NormFloat64()
+       float64(0.5229525761688676),   // NormFloat64()
+       float64(-1.5515595563231523),  // NormFloat64()
+       float64(0.0182029289376123),   // NormFloat64()
+       float64(-0.6820951356608795),  // NormFloat64()
+       float64(-0.5987943422687668),  // NormFloat64()
+       []int{},                                                     // Perm(0)
+       []int{0},                                                    // Perm(1)
+       []int{0, 4, 1, 3, 2},                                        // Perm(5)
+       []int{3, 1, 0, 4, 7, 5, 2, 6},                               // Perm(8)
+       []int{5, 0, 3, 6, 7, 4, 2, 1, 8},                            // Perm(9)
+       []int{4, 5, 0, 2, 6, 9, 3, 1, 8, 7},                         // Perm(10)
+       []int{14, 2, 0, 8, 3, 5, 13, 12, 1, 4, 6, 7, 11, 9, 15, 10}, // Perm(16)
+       []int{},                                                     // Perm(0)
+       []int{0},                                                    // Perm(1)
+       []int{3, 0, 1, 2, 4},                                        // Perm(5)
+       []int{5, 1, 2, 0, 4, 7, 3, 6},                               // Perm(8)
+       []int{4, 0, 6, 8, 1, 5, 2, 7, 3},                            // Perm(9)
+       []int{8, 6, 1, 7, 5, 4, 3, 2, 9, 0},                         // Perm(10)
+       []int{0, 3, 13, 2, 15, 4, 10, 1, 8, 14, 7, 6, 12, 9, 5, 11}, // Perm(16)
+       []int{},                             // Perm(0)
+       []int{0},                            // Perm(1)
+       []int{0, 4, 2, 1, 3},                // Perm(5)
+       []int{2, 1, 7, 0, 6, 3, 4, 5},       // Perm(8)
+       []int{8, 7, 5, 3, 4, 6, 0, 1, 2},    // Perm(9)
+       []int{1, 0, 2, 5, 7, 6, 9, 8, 3, 4}, // Perm(10)
+       uint32(4059586549),                  // Uint32()
+       uint32(1052117029),                  // Uint32()
+       uint32(2817310706),                  // Uint32()
+       uint32(233405013),                   // Uint32()
+       uint32(1578775030),                  // Uint32()
+       uint32(1243308993),                  // Uint32()
+       uint32(826517535),                   // Uint32()
+       uint32(2814630155),                  // Uint32()
+       uint32(3853314576),                  // Uint32()
+       uint32(718781857),                   // Uint32()
+       uint32(1239465936),                  // Uint32()
+       uint32(3876658295),                  // Uint32()
+       uint32(3649778518),                  // Uint32()
+       uint32(1172727096),                  // Uint32()
+       uint32(2615979505),                  // Uint32()
+       uint32(1089444252),                  // Uint32()
+       uint32(3327114623),                  // Uint32()
+       uint32(75079301),                    // Uint32()
+       uint32(3380456901),                  // Uint32()
+       uint32(3433369789),                  // Uint32()
+}