Skip to content

math/rand: rand.Float64 is not purely uniform #12290

Closed
@fabienlefloch

Description

@fabienlefloch

The current implementation (as of Go 1.5) of rand.Float64 is:

func (r *Rand) Float64() float64 {
  f := float64(r.Int63()) / (1 << 63)
  if f == 1 {
    f = 0
  }
  return f
}

As the mantissa of a Float64 is on 52 bits, there are a lot of numbers (2^11 ?) that were initially mapped to 1 that will effectively become 0. The Float64() method therefore does not preserve the uniform property of the underlying Int63() method.

This is related to issues #4965 and #6721 .

A better alternative, even if this implies to trade numbers under machine epsilon against uniformity comes from the prototypical code of Mersenne Twister 64 at http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/C-LANG/mt19937-64.c :

/* generates a random number on [0,1)-real-interval */
func (r *Rand) Float64() float64 {
    return (float64(r.Int63()) >> 10) * (1.0/9007199254740992.0)
}
/* generates a random number on (0,1)-real-interval */
func (r *Rand) Float64Open() float64 {
    return (float64(r.Int63())  >> 11) + 0.5) * (1.0/4503599627370496.0)
}

An interesting consequence is that this preserves symmetry around 0.5. It's also quite a bit faster on my machine, as the if statement of the original algorithm deteriorate performance significantly (not entirely sure why).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions