-
Notifications
You must be signed in to change notification settings - Fork 18.3k
Closed
Labels
Milestone
Description
by arnehormann:
What steps will reproduce the problem? See http://play.golang.org/p/uZw1CbxcMx It uses a constant source (returns seed, sets seed before each call to Float64) to inspect the distance between uint64 seeds and the generated float64 values. What is the expected output? The distance between all adjacent generated float64 values in range [0,1) should be the same and two adjacent uint64 should not be mapped to the same float64. The output of the referenced program should not print anything below the line "=======" What do you see instead? Intent: illustrate problems with http://golang.org/src/pkg/math/rand/rand.go?#L92 f0 (0) := 0p-1074 (0) f1 (1) := 4503599627370496p-115 (1.0842021724855044e-19) high (9223372036854775295) := 9007199254740991p-53 (0.9999999999999999) higher (9223372036854775296) := 4503599627370496p-52 (1) max (9223372036854775807) := 4503599627370496p-52 (1) ======= max == higher, but the source uint64 difference is 511 - the mapping uint63 to float64 has 'holes' for adjacent numbers distances between adjacent float64s are different - the largest difference is 8998403161718784p-106 (1.109138822452671e-16) which is more than f1 - f0: 4503599627370496p-115 (1.0842021724855044e-19) Which compiler are you using (5g, 6g, 8g, gccgo)? play.golang.org and locally 6g Which operating system are you using? OS X Mountain Lion Which version are you using? (run 'go version') go version devel +b0c8d47acfc5 Sat Mar 02 10:41:53 2013 +0400 darwin/amd64 Please provide any additional information below. The calculation used in rand.Float64 (and Float32) uses 63 (31) bits of the passed integer although the mantissa is only 52 (23) bits long. The binary representation is not able to contain all possible values and creates a "kind of" logarithmic distribution with an increasing amount of ints mapped to the same float the nearer it gets to 1.0. Not having an uniform distribution and this bias probably also influence the creation of numbers in different (e.g. normal) distributions. I propose using one of these ways: either create a number in [1,2) (which are always in uniform distribution) and subtract 1: func UniformFloat64(b uint64) float64 { const range_1_2Exp = 0x3ff0000000000000 const mantissaMask = 0x000fffffffffffff return math.Float64frombits(range_1_2Exp|(mantissaMask&b)) - 1.0 } or create the smallest legal "step" and multiply it with a number masked to be in range [0, max mantissa]: func UniformFloat64(b uint64) float64 { const mantissaMask = 0x000fffffffffffff step := math.Float64frombits(0x3cb0000000000000) return step * float64(mantissaMask&b) } Both create the same float for the same input, I didn't benchmark them yet. I have some tests and code ready if you are interested, I only need to know if the existing functions can safely be replaced or there may be some weird dependency on their behavior.