Generate uniformly random float which can return all possible values
An easy way to generate a random float64 in [0,1) is by generating a uniformly random int in [0,2⁵³) and dividing it by 2⁵³. This is essentially what rand.Float64()
is doing.
However, not all possible float64 values between 0 and 1 can be generated this way: if the value is lower than 2⁻⁴ for example, the 4 last bits of the significand are always going to be 0. Or, put more simply, the naive method always returns multiples of 2⁻⁵³, and not all floating point numbers between 0 and 1 are multiples of 2⁻⁵³.
How do you generate a uniformly random float64 such as every possible value has a chance of being returned? (Here, uniformly random means over the real interval [0,1): conceptually, I want to pick a uniformly random real number between 0 and 1 and return the closest float.)
For context, I need this because I'm implementing this paper and the assumption "all possible values between 0 and 1 are represented" is essential for the result to hold.
go random floating-point
|
show 4 more comments
An easy way to generate a random float64 in [0,1) is by generating a uniformly random int in [0,2⁵³) and dividing it by 2⁵³. This is essentially what rand.Float64()
is doing.
However, not all possible float64 values between 0 and 1 can be generated this way: if the value is lower than 2⁻⁴ for example, the 4 last bits of the significand are always going to be 0. Or, put more simply, the naive method always returns multiples of 2⁻⁵³, and not all floating point numbers between 0 and 1 are multiples of 2⁻⁵³.
How do you generate a uniformly random float64 such as every possible value has a chance of being returned? (Here, uniformly random means over the real interval [0,1): conceptually, I want to pick a uniformly random real number between 0 and 1 and return the closest float.)
For context, I need this because I'm implementing this paper and the assumption "all possible values between 0 and 1 are represented" is essential for the result to hold.
go random floating-point
rand.Float64()
is probably the closest in-built way of doing it golang.org/pkg/math/rand/#Rand.Float64
– Carlos Gonzalez
Nov 13 '18 at 9:00
1
No,rand.Float64()
does it the naive way and doesn't return every possible value. I edited the question to add a link.
– Ted
Nov 13 '18 at 9:08
with the chance of getting 1 being 1/2⁵³ i don't think you need to worry about it, if you do worry about it the docs you linked under cincenzo's answer explain how to fix it. so you can implement rand float 64 yourself
– Pizza lord
Nov 13 '18 at 9:13
1
You probably have to implement this yourself. mumble.net/~campbell/2014/04/28 might be a starting point.
– Ralf Stubner
Nov 13 '18 at 9:13
1
The paper tells you what to generate: “A uniform distribution over D ∩ (0, 1) can be generated by independently sampling an exponent (from the geometric distribution with parameter .5) and a significand (by drawing a uniform string from 0, 1⁵²).” That omits discussion of subnormals. I suspect covering the normals suffices for their purposes, but completing the geometric distribution by duplicating the probability for the least normal exponent and the subnormal values would fit nicely. (The probability of each floating-point number in [0, 1) would be proportional to its ULP.)
– Eric Postpischil
Nov 15 '18 at 2:13
|
show 4 more comments
An easy way to generate a random float64 in [0,1) is by generating a uniformly random int in [0,2⁵³) and dividing it by 2⁵³. This is essentially what rand.Float64()
is doing.
However, not all possible float64 values between 0 and 1 can be generated this way: if the value is lower than 2⁻⁴ for example, the 4 last bits of the significand are always going to be 0. Or, put more simply, the naive method always returns multiples of 2⁻⁵³, and not all floating point numbers between 0 and 1 are multiples of 2⁻⁵³.
How do you generate a uniformly random float64 such as every possible value has a chance of being returned? (Here, uniformly random means over the real interval [0,1): conceptually, I want to pick a uniformly random real number between 0 and 1 and return the closest float.)
For context, I need this because I'm implementing this paper and the assumption "all possible values between 0 and 1 are represented" is essential for the result to hold.
go random floating-point
An easy way to generate a random float64 in [0,1) is by generating a uniformly random int in [0,2⁵³) and dividing it by 2⁵³. This is essentially what rand.Float64()
is doing.
However, not all possible float64 values between 0 and 1 can be generated this way: if the value is lower than 2⁻⁴ for example, the 4 last bits of the significand are always going to be 0. Or, put more simply, the naive method always returns multiples of 2⁻⁵³, and not all floating point numbers between 0 and 1 are multiples of 2⁻⁵³.
How do you generate a uniformly random float64 such as every possible value has a chance of being returned? (Here, uniformly random means over the real interval [0,1): conceptually, I want to pick a uniformly random real number between 0 and 1 and return the closest float.)
For context, I need this because I'm implementing this paper and the assumption "all possible values between 0 and 1 are represented" is essential for the result to hold.
go random floating-point
go random floating-point
edited Nov 14 '18 at 7:33
Ted
asked Nov 13 '18 at 8:52
TedTed
443414
443414
rand.Float64()
is probably the closest in-built way of doing it golang.org/pkg/math/rand/#Rand.Float64
– Carlos Gonzalez
Nov 13 '18 at 9:00
1
No,rand.Float64()
does it the naive way and doesn't return every possible value. I edited the question to add a link.
– Ted
Nov 13 '18 at 9:08
with the chance of getting 1 being 1/2⁵³ i don't think you need to worry about it, if you do worry about it the docs you linked under cincenzo's answer explain how to fix it. so you can implement rand float 64 yourself
– Pizza lord
Nov 13 '18 at 9:13
1
You probably have to implement this yourself. mumble.net/~campbell/2014/04/28 might be a starting point.
– Ralf Stubner
Nov 13 '18 at 9:13
1
The paper tells you what to generate: “A uniform distribution over D ∩ (0, 1) can be generated by independently sampling an exponent (from the geometric distribution with parameter .5) and a significand (by drawing a uniform string from 0, 1⁵²).” That omits discussion of subnormals. I suspect covering the normals suffices for their purposes, but completing the geometric distribution by duplicating the probability for the least normal exponent and the subnormal values would fit nicely. (The probability of each floating-point number in [0, 1) would be proportional to its ULP.)
– Eric Postpischil
Nov 15 '18 at 2:13
|
show 4 more comments
rand.Float64()
is probably the closest in-built way of doing it golang.org/pkg/math/rand/#Rand.Float64
– Carlos Gonzalez
Nov 13 '18 at 9:00
1
No,rand.Float64()
does it the naive way and doesn't return every possible value. I edited the question to add a link.
– Ted
Nov 13 '18 at 9:08
with the chance of getting 1 being 1/2⁵³ i don't think you need to worry about it, if you do worry about it the docs you linked under cincenzo's answer explain how to fix it. so you can implement rand float 64 yourself
– Pizza lord
Nov 13 '18 at 9:13
1
You probably have to implement this yourself. mumble.net/~campbell/2014/04/28 might be a starting point.
– Ralf Stubner
Nov 13 '18 at 9:13
1
The paper tells you what to generate: “A uniform distribution over D ∩ (0, 1) can be generated by independently sampling an exponent (from the geometric distribution with parameter .5) and a significand (by drawing a uniform string from 0, 1⁵²).” That omits discussion of subnormals. I suspect covering the normals suffices for their purposes, but completing the geometric distribution by duplicating the probability for the least normal exponent and the subnormal values would fit nicely. (The probability of each floating-point number in [0, 1) would be proportional to its ULP.)
– Eric Postpischil
Nov 15 '18 at 2:13
rand.Float64()
is probably the closest in-built way of doing it golang.org/pkg/math/rand/#Rand.Float64– Carlos Gonzalez
Nov 13 '18 at 9:00
rand.Float64()
is probably the closest in-built way of doing it golang.org/pkg/math/rand/#Rand.Float64– Carlos Gonzalez
Nov 13 '18 at 9:00
1
1
No,
rand.Float64()
does it the naive way and doesn't return every possible value. I edited the question to add a link.– Ted
Nov 13 '18 at 9:08
No,
rand.Float64()
does it the naive way and doesn't return every possible value. I edited the question to add a link.– Ted
Nov 13 '18 at 9:08
with the chance of getting 1 being 1/2⁵³ i don't think you need to worry about it, if you do worry about it the docs you linked under cincenzo's answer explain how to fix it. so you can implement rand float 64 yourself
– Pizza lord
Nov 13 '18 at 9:13
with the chance of getting 1 being 1/2⁵³ i don't think you need to worry about it, if you do worry about it the docs you linked under cincenzo's answer explain how to fix it. so you can implement rand float 64 yourself
– Pizza lord
Nov 13 '18 at 9:13
1
1
You probably have to implement this yourself. mumble.net/~campbell/2014/04/28 might be a starting point.
– Ralf Stubner
Nov 13 '18 at 9:13
You probably have to implement this yourself. mumble.net/~campbell/2014/04/28 might be a starting point.
– Ralf Stubner
Nov 13 '18 at 9:13
1
1
The paper tells you what to generate: “A uniform distribution over D ∩ (0, 1) can be generated by independently sampling an exponent (from the geometric distribution with parameter .5) and a significand (by drawing a uniform string from 0, 1⁵²).” That omits discussion of subnormals. I suspect covering the normals suffices for their purposes, but completing the geometric distribution by duplicating the probability for the least normal exponent and the subnormal values would fit nicely. (The probability of each floating-point number in [0, 1) would be proportional to its ULP.)
– Eric Postpischil
Nov 15 '18 at 2:13
The paper tells you what to generate: “A uniform distribution over D ∩ (0, 1) can be generated by independently sampling an exponent (from the geometric distribution with parameter .5) and a significand (by drawing a uniform string from 0, 1⁵²).” That omits discussion of subnormals. I suspect covering the normals suffices for their purposes, but completing the geometric distribution by duplicating the probability for the least normal exponent and the subnormal values would fit nicely. (The probability of each floating-point number in [0, 1) would be proportional to its ULP.)
– Eric Postpischil
Nov 15 '18 at 2:13
|
show 4 more comments
5 Answers
5
active
oldest
votes
Because the binary64 floating point numbers are not uniformly spaced, you cannot generate a uniform distribution which can return all possible values less that 1.
If you omit the requirement uniform you have to generate all representable multiples of the smallest positive denormal number 2^(-1074)
and zero.
I don't understand this comment. Essentially, I want do as if I were selecting a real number uniformly randomly between 0 and 1 and rounding it to the next float. I think Severin's answer is what I'm looking for. I'll implement it in Go.
– Ted
Nov 13 '18 at 15:09
You should add this to your question, but be aware that your mapping can generate 1 for values in the interval (1-2^(-53), 1)
– gammatester
Nov 13 '18 at 15:40
add a comment |
Porting this code (suggested in Severin's answer) is a possible option.
I think that it is equivalent to first generate the significand bits (by generating a random float in [1,2)), and then choose the exponent from a geometric distribution (it has a 0.5 chance of being -1, 0.25 of being -2, etc.).
// uniform returns a uniformly random float in [0,1).
func uniform() float64
sig := rand.Uint64() % (1 << 52)
return (1 + float64(i)/(1<<52)) / math.Pow(2, geometric())
// geometric returns a number picked from a geometric
// distribution of parameter 0.5.
func geometric() float64
b := 1
for rand.Uint64()%2 == 0
b++
return b
We can probably make geometric() faster by using one of the LeadingZeros*
functions from the bits
package instead of doing one coin flip per bit.
If you're going to port code directly, things would depend on ldexp() implementation. I quickly looked through Go runtime and glibc implementation, and there are quite some differences to my surprise.
– Severin Pappadeux
Nov 14 '18 at 0:50
Yes, that's why I'm taking another approach for the implementation. I think both are conceptually equivalent (but would be happy to be proven wrong).
– Ted
Nov 14 '18 at 7:31
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 '18 at 16:27
add a comment |
Well, standard way, I believe, is to generate up to 1074bits integer and map it to the double. Beware, that your RNG should have internal state at least 1074bits long.
Reference implementation: http://xoshiro.di.unimi.it/random_real.c
Discussion about it: http://xoshiro.di.unimi.it/
Another good link: https://lemire.me/blog/2017/02/28/how-many-floating-point-numbers-are-in-the-interval-01/
(a) That is a way, not “the standard” way. (b) Its range is [0, 1], but the question asks for [0, 1). It does mention the range can be reduced by rejection sampling (retry if the result is 1). (c) It generates 1074 only conceptually; in practice it uses many smaller bits in almost all cases.
– Eric Postpischil
Nov 13 '18 at 16:35
@EricPostpischil(a)
Doesn't seems to be a better way.(b)
yep, rejection comes to rescue, I believe it is mention in comments.(c)
yes, thank you, I corrected the answer.
– Severin Pappadeux
Nov 13 '18 at 18:16
add a comment |
You could use brute-force rejection sampling by generating 16 random bytes and using it only if it's a valid float64 in [0,1)
. This approach should give you a normal distribution of all values in that range with performance not much worse than other strategies based on simple benchmarking.
For example (Go Playground):
import "math/rand"
func randFloat64() float64
for
f := math.Float64frombits(rand.Uint64())
if f >= 0 && f < 1.0
return f
If performance is critical then you could build an enormous lookup table containing only the valid numbers and choose a random location in the table. The table could be generated ahead of time in a similar fashion as above, by enumerating the bitfield and storing only valid numbers.
Thanks — but this generates a uniform distribution over the possible elements, not over the real interval. I clarified the question.
– Ted
Nov 14 '18 at 7:35
add a comment |
You can use rand Float64 function to return float in the [0, 1) range:
package main
import (
"fmt"
"math/rand"
)
func main()
fmt.Println(rand.Float64())
From the docs:
Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) from the default Source.
Precisely, no: golang.org/src/math/rand/rand.go?s=5359:5391#L168 doesn't return all possible values.
– Ted
Nov 13 '18 at 9:06
From the source comment: "Instead of that, if we round up to 1, just try again. Getting 1 only happens 1/2⁵³ of the time, so most clients will not observe it anyway.". They say they taken into account this.
– Vincenzo Maggio
Nov 13 '18 at 9:08
That's not the problem. The problem is that this code only returns multiples of 2⁻⁵³. Not all floating-point numbers between 0 and 1 are multiples of 2⁻⁵³, so you don't return all of them this way.
– Ted
Nov 13 '18 at 9:10
Listen, here's a discussion with even Russ Cox itself intervining: github.com/golang/go/issues/12290 saying this is not a huge problem. Thanks for the downvote.
– Vincenzo Maggio
Nov 13 '18 at 9:14
I added a comment explaining why I have a good reason not to use the code from the standard library. Thanks for making me notice that the rationale for my question was unclear! =)
– Ted
Nov 13 '18 at 9:20
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53277105%2fgenerate-uniformly-random-float-which-can-return-all-possible-values%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
Because the binary64 floating point numbers are not uniformly spaced, you cannot generate a uniform distribution which can return all possible values less that 1.
If you omit the requirement uniform you have to generate all representable multiples of the smallest positive denormal number 2^(-1074)
and zero.
I don't understand this comment. Essentially, I want do as if I were selecting a real number uniformly randomly between 0 and 1 and rounding it to the next float. I think Severin's answer is what I'm looking for. I'll implement it in Go.
– Ted
Nov 13 '18 at 15:09
You should add this to your question, but be aware that your mapping can generate 1 for values in the interval (1-2^(-53), 1)
– gammatester
Nov 13 '18 at 15:40
add a comment |
Because the binary64 floating point numbers are not uniformly spaced, you cannot generate a uniform distribution which can return all possible values less that 1.
If you omit the requirement uniform you have to generate all representable multiples of the smallest positive denormal number 2^(-1074)
and zero.
I don't understand this comment. Essentially, I want do as if I were selecting a real number uniformly randomly between 0 and 1 and rounding it to the next float. I think Severin's answer is what I'm looking for. I'll implement it in Go.
– Ted
Nov 13 '18 at 15:09
You should add this to your question, but be aware that your mapping can generate 1 for values in the interval (1-2^(-53), 1)
– gammatester
Nov 13 '18 at 15:40
add a comment |
Because the binary64 floating point numbers are not uniformly spaced, you cannot generate a uniform distribution which can return all possible values less that 1.
If you omit the requirement uniform you have to generate all representable multiples of the smallest positive denormal number 2^(-1074)
and zero.
Because the binary64 floating point numbers are not uniformly spaced, you cannot generate a uniform distribution which can return all possible values less that 1.
If you omit the requirement uniform you have to generate all representable multiples of the smallest positive denormal number 2^(-1074)
and zero.
answered Nov 13 '18 at 9:42
gammatestergammatester
9181712
9181712
I don't understand this comment. Essentially, I want do as if I were selecting a real number uniformly randomly between 0 and 1 and rounding it to the next float. I think Severin's answer is what I'm looking for. I'll implement it in Go.
– Ted
Nov 13 '18 at 15:09
You should add this to your question, but be aware that your mapping can generate 1 for values in the interval (1-2^(-53), 1)
– gammatester
Nov 13 '18 at 15:40
add a comment |
I don't understand this comment. Essentially, I want do as if I were selecting a real number uniformly randomly between 0 and 1 and rounding it to the next float. I think Severin's answer is what I'm looking for. I'll implement it in Go.
– Ted
Nov 13 '18 at 15:09
You should add this to your question, but be aware that your mapping can generate 1 for values in the interval (1-2^(-53), 1)
– gammatester
Nov 13 '18 at 15:40
I don't understand this comment. Essentially, I want do as if I were selecting a real number uniformly randomly between 0 and 1 and rounding it to the next float. I think Severin's answer is what I'm looking for. I'll implement it in Go.
– Ted
Nov 13 '18 at 15:09
I don't understand this comment. Essentially, I want do as if I were selecting a real number uniformly randomly between 0 and 1 and rounding it to the next float. I think Severin's answer is what I'm looking for. I'll implement it in Go.
– Ted
Nov 13 '18 at 15:09
You should add this to your question, but be aware that your mapping can generate 1 for values in the interval (1-2^(-53), 1)
– gammatester
Nov 13 '18 at 15:40
You should add this to your question, but be aware that your mapping can generate 1 for values in the interval (1-2^(-53), 1)
– gammatester
Nov 13 '18 at 15:40
add a comment |
Porting this code (suggested in Severin's answer) is a possible option.
I think that it is equivalent to first generate the significand bits (by generating a random float in [1,2)), and then choose the exponent from a geometric distribution (it has a 0.5 chance of being -1, 0.25 of being -2, etc.).
// uniform returns a uniformly random float in [0,1).
func uniform() float64
sig := rand.Uint64() % (1 << 52)
return (1 + float64(i)/(1<<52)) / math.Pow(2, geometric())
// geometric returns a number picked from a geometric
// distribution of parameter 0.5.
func geometric() float64
b := 1
for rand.Uint64()%2 == 0
b++
return b
We can probably make geometric() faster by using one of the LeadingZeros*
functions from the bits
package instead of doing one coin flip per bit.
If you're going to port code directly, things would depend on ldexp() implementation. I quickly looked through Go runtime and glibc implementation, and there are quite some differences to my surprise.
– Severin Pappadeux
Nov 14 '18 at 0:50
Yes, that's why I'm taking another approach for the implementation. I think both are conceptually equivalent (but would be happy to be proven wrong).
– Ted
Nov 14 '18 at 7:31
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 '18 at 16:27
add a comment |
Porting this code (suggested in Severin's answer) is a possible option.
I think that it is equivalent to first generate the significand bits (by generating a random float in [1,2)), and then choose the exponent from a geometric distribution (it has a 0.5 chance of being -1, 0.25 of being -2, etc.).
// uniform returns a uniformly random float in [0,1).
func uniform() float64
sig := rand.Uint64() % (1 << 52)
return (1 + float64(i)/(1<<52)) / math.Pow(2, geometric())
// geometric returns a number picked from a geometric
// distribution of parameter 0.5.
func geometric() float64
b := 1
for rand.Uint64()%2 == 0
b++
return b
We can probably make geometric() faster by using one of the LeadingZeros*
functions from the bits
package instead of doing one coin flip per bit.
If you're going to port code directly, things would depend on ldexp() implementation. I quickly looked through Go runtime and glibc implementation, and there are quite some differences to my surprise.
– Severin Pappadeux
Nov 14 '18 at 0:50
Yes, that's why I'm taking another approach for the implementation. I think both are conceptually equivalent (but would be happy to be proven wrong).
– Ted
Nov 14 '18 at 7:31
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 '18 at 16:27
add a comment |
Porting this code (suggested in Severin's answer) is a possible option.
I think that it is equivalent to first generate the significand bits (by generating a random float in [1,2)), and then choose the exponent from a geometric distribution (it has a 0.5 chance of being -1, 0.25 of being -2, etc.).
// uniform returns a uniformly random float in [0,1).
func uniform() float64
sig := rand.Uint64() % (1 << 52)
return (1 + float64(i)/(1<<52)) / math.Pow(2, geometric())
// geometric returns a number picked from a geometric
// distribution of parameter 0.5.
func geometric() float64
b := 1
for rand.Uint64()%2 == 0
b++
return b
We can probably make geometric() faster by using one of the LeadingZeros*
functions from the bits
package instead of doing one coin flip per bit.
Porting this code (suggested in Severin's answer) is a possible option.
I think that it is equivalent to first generate the significand bits (by generating a random float in [1,2)), and then choose the exponent from a geometric distribution (it has a 0.5 chance of being -1, 0.25 of being -2, etc.).
// uniform returns a uniformly random float in [0,1).
func uniform() float64
sig := rand.Uint64() % (1 << 52)
return (1 + float64(i)/(1<<52)) / math.Pow(2, geometric())
// geometric returns a number picked from a geometric
// distribution of parameter 0.5.
func geometric() float64
b := 1
for rand.Uint64()%2 == 0
b++
return b
We can probably make geometric() faster by using one of the LeadingZeros*
functions from the bits
package instead of doing one coin flip per bit.
answered Nov 13 '18 at 17:37
TedTed
443414
443414
If you're going to port code directly, things would depend on ldexp() implementation. I quickly looked through Go runtime and glibc implementation, and there are quite some differences to my surprise.
– Severin Pappadeux
Nov 14 '18 at 0:50
Yes, that's why I'm taking another approach for the implementation. I think both are conceptually equivalent (but would be happy to be proven wrong).
– Ted
Nov 14 '18 at 7:31
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 '18 at 16:27
add a comment |
If you're going to port code directly, things would depend on ldexp() implementation. I quickly looked through Go runtime and glibc implementation, and there are quite some differences to my surprise.
– Severin Pappadeux
Nov 14 '18 at 0:50
Yes, that's why I'm taking another approach for the implementation. I think both are conceptually equivalent (but would be happy to be proven wrong).
– Ted
Nov 14 '18 at 7:31
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 '18 at 16:27
If you're going to port code directly, things would depend on ldexp() implementation. I quickly looked through Go runtime and glibc implementation, and there are quite some differences to my surprise.
– Severin Pappadeux
Nov 14 '18 at 0:50
If you're going to port code directly, things would depend on ldexp() implementation. I quickly looked through Go runtime and glibc implementation, and there are quite some differences to my surprise.
– Severin Pappadeux
Nov 14 '18 at 0:50
Yes, that's why I'm taking another approach for the implementation. I think both are conceptually equivalent (but would be happy to be proven wrong).
– Ted
Nov 14 '18 at 7:31
Yes, that's why I'm taking another approach for the implementation. I think both are conceptually equivalent (but would be happy to be proven wrong).
– Ted
Nov 14 '18 at 7:31
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 '18 at 16:27
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 '18 at 16:27
add a comment |
Well, standard way, I believe, is to generate up to 1074bits integer and map it to the double. Beware, that your RNG should have internal state at least 1074bits long.
Reference implementation: http://xoshiro.di.unimi.it/random_real.c
Discussion about it: http://xoshiro.di.unimi.it/
Another good link: https://lemire.me/blog/2017/02/28/how-many-floating-point-numbers-are-in-the-interval-01/
(a) That is a way, not “the standard” way. (b) Its range is [0, 1], but the question asks for [0, 1). It does mention the range can be reduced by rejection sampling (retry if the result is 1). (c) It generates 1074 only conceptually; in practice it uses many smaller bits in almost all cases.
– Eric Postpischil
Nov 13 '18 at 16:35
@EricPostpischil(a)
Doesn't seems to be a better way.(b)
yep, rejection comes to rescue, I believe it is mention in comments.(c)
yes, thank you, I corrected the answer.
– Severin Pappadeux
Nov 13 '18 at 18:16
add a comment |
Well, standard way, I believe, is to generate up to 1074bits integer and map it to the double. Beware, that your RNG should have internal state at least 1074bits long.
Reference implementation: http://xoshiro.di.unimi.it/random_real.c
Discussion about it: http://xoshiro.di.unimi.it/
Another good link: https://lemire.me/blog/2017/02/28/how-many-floating-point-numbers-are-in-the-interval-01/
(a) That is a way, not “the standard” way. (b) Its range is [0, 1], but the question asks for [0, 1). It does mention the range can be reduced by rejection sampling (retry if the result is 1). (c) It generates 1074 only conceptually; in practice it uses many smaller bits in almost all cases.
– Eric Postpischil
Nov 13 '18 at 16:35
@EricPostpischil(a)
Doesn't seems to be a better way.(b)
yep, rejection comes to rescue, I believe it is mention in comments.(c)
yes, thank you, I corrected the answer.
– Severin Pappadeux
Nov 13 '18 at 18:16
add a comment |
Well, standard way, I believe, is to generate up to 1074bits integer and map it to the double. Beware, that your RNG should have internal state at least 1074bits long.
Reference implementation: http://xoshiro.di.unimi.it/random_real.c
Discussion about it: http://xoshiro.di.unimi.it/
Another good link: https://lemire.me/blog/2017/02/28/how-many-floating-point-numbers-are-in-the-interval-01/
Well, standard way, I believe, is to generate up to 1074bits integer and map it to the double. Beware, that your RNG should have internal state at least 1074bits long.
Reference implementation: http://xoshiro.di.unimi.it/random_real.c
Discussion about it: http://xoshiro.di.unimi.it/
Another good link: https://lemire.me/blog/2017/02/28/how-many-floating-point-numbers-are-in-the-interval-01/
edited Dec 3 '18 at 16:27
answered Nov 13 '18 at 14:30
Severin PappadeuxSeverin Pappadeux
9,49121432
9,49121432
(a) That is a way, not “the standard” way. (b) Its range is [0, 1], but the question asks for [0, 1). It does mention the range can be reduced by rejection sampling (retry if the result is 1). (c) It generates 1074 only conceptually; in practice it uses many smaller bits in almost all cases.
– Eric Postpischil
Nov 13 '18 at 16:35
@EricPostpischil(a)
Doesn't seems to be a better way.(b)
yep, rejection comes to rescue, I believe it is mention in comments.(c)
yes, thank you, I corrected the answer.
– Severin Pappadeux
Nov 13 '18 at 18:16
add a comment |
(a) That is a way, not “the standard” way. (b) Its range is [0, 1], but the question asks for [0, 1). It does mention the range can be reduced by rejection sampling (retry if the result is 1). (c) It generates 1074 only conceptually; in practice it uses many smaller bits in almost all cases.
– Eric Postpischil
Nov 13 '18 at 16:35
@EricPostpischil(a)
Doesn't seems to be a better way.(b)
yep, rejection comes to rescue, I believe it is mention in comments.(c)
yes, thank you, I corrected the answer.
– Severin Pappadeux
Nov 13 '18 at 18:16
(a) That is a way, not “the standard” way. (b) Its range is [0, 1], but the question asks for [0, 1). It does mention the range can be reduced by rejection sampling (retry if the result is 1). (c) It generates 1074 only conceptually; in practice it uses many smaller bits in almost all cases.
– Eric Postpischil
Nov 13 '18 at 16:35
(a) That is a way, not “the standard” way. (b) Its range is [0, 1], but the question asks for [0, 1). It does mention the range can be reduced by rejection sampling (retry if the result is 1). (c) It generates 1074 only conceptually; in practice it uses many smaller bits in almost all cases.
– Eric Postpischil
Nov 13 '18 at 16:35
@EricPostpischil
(a)
Doesn't seems to be a better way. (b)
yep, rejection comes to rescue, I believe it is mention in comments. (c)
yes, thank you, I corrected the answer.– Severin Pappadeux
Nov 13 '18 at 18:16
@EricPostpischil
(a)
Doesn't seems to be a better way. (b)
yep, rejection comes to rescue, I believe it is mention in comments. (c)
yes, thank you, I corrected the answer.– Severin Pappadeux
Nov 13 '18 at 18:16
add a comment |
You could use brute-force rejection sampling by generating 16 random bytes and using it only if it's a valid float64 in [0,1)
. This approach should give you a normal distribution of all values in that range with performance not much worse than other strategies based on simple benchmarking.
For example (Go Playground):
import "math/rand"
func randFloat64() float64
for
f := math.Float64frombits(rand.Uint64())
if f >= 0 && f < 1.0
return f
If performance is critical then you could build an enormous lookup table containing only the valid numbers and choose a random location in the table. The table could be generated ahead of time in a similar fashion as above, by enumerating the bitfield and storing only valid numbers.
Thanks — but this generates a uniform distribution over the possible elements, not over the real interval. I clarified the question.
– Ted
Nov 14 '18 at 7:35
add a comment |
You could use brute-force rejection sampling by generating 16 random bytes and using it only if it's a valid float64 in [0,1)
. This approach should give you a normal distribution of all values in that range with performance not much worse than other strategies based on simple benchmarking.
For example (Go Playground):
import "math/rand"
func randFloat64() float64
for
f := math.Float64frombits(rand.Uint64())
if f >= 0 && f < 1.0
return f
If performance is critical then you could build an enormous lookup table containing only the valid numbers and choose a random location in the table. The table could be generated ahead of time in a similar fashion as above, by enumerating the bitfield and storing only valid numbers.
Thanks — but this generates a uniform distribution over the possible elements, not over the real interval. I clarified the question.
– Ted
Nov 14 '18 at 7:35
add a comment |
You could use brute-force rejection sampling by generating 16 random bytes and using it only if it's a valid float64 in [0,1)
. This approach should give you a normal distribution of all values in that range with performance not much worse than other strategies based on simple benchmarking.
For example (Go Playground):
import "math/rand"
func randFloat64() float64
for
f := math.Float64frombits(rand.Uint64())
if f >= 0 && f < 1.0
return f
If performance is critical then you could build an enormous lookup table containing only the valid numbers and choose a random location in the table. The table could be generated ahead of time in a similar fashion as above, by enumerating the bitfield and storing only valid numbers.
You could use brute-force rejection sampling by generating 16 random bytes and using it only if it's a valid float64 in [0,1)
. This approach should give you a normal distribution of all values in that range with performance not much worse than other strategies based on simple benchmarking.
For example (Go Playground):
import "math/rand"
func randFloat64() float64
for
f := math.Float64frombits(rand.Uint64())
if f >= 0 && f < 1.0
return f
If performance is critical then you could build an enormous lookup table containing only the valid numbers and choose a random location in the table. The table could be generated ahead of time in a similar fashion as above, by enumerating the bitfield and storing only valid numbers.
edited Nov 13 '18 at 20:37
answered Nov 13 '18 at 18:12
maericsmaerics
105k29202249
105k29202249
Thanks — but this generates a uniform distribution over the possible elements, not over the real interval. I clarified the question.
– Ted
Nov 14 '18 at 7:35
add a comment |
Thanks — but this generates a uniform distribution over the possible elements, not over the real interval. I clarified the question.
– Ted
Nov 14 '18 at 7:35
Thanks — but this generates a uniform distribution over the possible elements, not over the real interval. I clarified the question.
– Ted
Nov 14 '18 at 7:35
Thanks — but this generates a uniform distribution over the possible elements, not over the real interval. I clarified the question.
– Ted
Nov 14 '18 at 7:35
add a comment |
You can use rand Float64 function to return float in the [0, 1) range:
package main
import (
"fmt"
"math/rand"
)
func main()
fmt.Println(rand.Float64())
From the docs:
Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) from the default Source.
Precisely, no: golang.org/src/math/rand/rand.go?s=5359:5391#L168 doesn't return all possible values.
– Ted
Nov 13 '18 at 9:06
From the source comment: "Instead of that, if we round up to 1, just try again. Getting 1 only happens 1/2⁵³ of the time, so most clients will not observe it anyway.". They say they taken into account this.
– Vincenzo Maggio
Nov 13 '18 at 9:08
That's not the problem. The problem is that this code only returns multiples of 2⁻⁵³. Not all floating-point numbers between 0 and 1 are multiples of 2⁻⁵³, so you don't return all of them this way.
– Ted
Nov 13 '18 at 9:10
Listen, here's a discussion with even Russ Cox itself intervining: github.com/golang/go/issues/12290 saying this is not a huge problem. Thanks for the downvote.
– Vincenzo Maggio
Nov 13 '18 at 9:14
I added a comment explaining why I have a good reason not to use the code from the standard library. Thanks for making me notice that the rationale for my question was unclear! =)
– Ted
Nov 13 '18 at 9:20
add a comment |
You can use rand Float64 function to return float in the [0, 1) range:
package main
import (
"fmt"
"math/rand"
)
func main()
fmt.Println(rand.Float64())
From the docs:
Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) from the default Source.
Precisely, no: golang.org/src/math/rand/rand.go?s=5359:5391#L168 doesn't return all possible values.
– Ted
Nov 13 '18 at 9:06
From the source comment: "Instead of that, if we round up to 1, just try again. Getting 1 only happens 1/2⁵³ of the time, so most clients will not observe it anyway.". They say they taken into account this.
– Vincenzo Maggio
Nov 13 '18 at 9:08
That's not the problem. The problem is that this code only returns multiples of 2⁻⁵³. Not all floating-point numbers between 0 and 1 are multiples of 2⁻⁵³, so you don't return all of them this way.
– Ted
Nov 13 '18 at 9:10
Listen, here's a discussion with even Russ Cox itself intervining: github.com/golang/go/issues/12290 saying this is not a huge problem. Thanks for the downvote.
– Vincenzo Maggio
Nov 13 '18 at 9:14
I added a comment explaining why I have a good reason not to use the code from the standard library. Thanks for making me notice that the rationale for my question was unclear! =)
– Ted
Nov 13 '18 at 9:20
add a comment |
You can use rand Float64 function to return float in the [0, 1) range:
package main
import (
"fmt"
"math/rand"
)
func main()
fmt.Println(rand.Float64())
From the docs:
Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) from the default Source.
You can use rand Float64 function to return float in the [0, 1) range:
package main
import (
"fmt"
"math/rand"
)
func main()
fmt.Println(rand.Float64())
From the docs:
Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) from the default Source.
answered Nov 13 '18 at 9:02
Vincenzo MaggioVincenzo Maggio
3,1152040
3,1152040
Precisely, no: golang.org/src/math/rand/rand.go?s=5359:5391#L168 doesn't return all possible values.
– Ted
Nov 13 '18 at 9:06
From the source comment: "Instead of that, if we round up to 1, just try again. Getting 1 only happens 1/2⁵³ of the time, so most clients will not observe it anyway.". They say they taken into account this.
– Vincenzo Maggio
Nov 13 '18 at 9:08
That's not the problem. The problem is that this code only returns multiples of 2⁻⁵³. Not all floating-point numbers between 0 and 1 are multiples of 2⁻⁵³, so you don't return all of them this way.
– Ted
Nov 13 '18 at 9:10
Listen, here's a discussion with even Russ Cox itself intervining: github.com/golang/go/issues/12290 saying this is not a huge problem. Thanks for the downvote.
– Vincenzo Maggio
Nov 13 '18 at 9:14
I added a comment explaining why I have a good reason not to use the code from the standard library. Thanks for making me notice that the rationale for my question was unclear! =)
– Ted
Nov 13 '18 at 9:20
add a comment |
Precisely, no: golang.org/src/math/rand/rand.go?s=5359:5391#L168 doesn't return all possible values.
– Ted
Nov 13 '18 at 9:06
From the source comment: "Instead of that, if we round up to 1, just try again. Getting 1 only happens 1/2⁵³ of the time, so most clients will not observe it anyway.". They say they taken into account this.
– Vincenzo Maggio
Nov 13 '18 at 9:08
That's not the problem. The problem is that this code only returns multiples of 2⁻⁵³. Not all floating-point numbers between 0 and 1 are multiples of 2⁻⁵³, so you don't return all of them this way.
– Ted
Nov 13 '18 at 9:10
Listen, here's a discussion with even Russ Cox itself intervining: github.com/golang/go/issues/12290 saying this is not a huge problem. Thanks for the downvote.
– Vincenzo Maggio
Nov 13 '18 at 9:14
I added a comment explaining why I have a good reason not to use the code from the standard library. Thanks for making me notice that the rationale for my question was unclear! =)
– Ted
Nov 13 '18 at 9:20
Precisely, no: golang.org/src/math/rand/rand.go?s=5359:5391#L168 doesn't return all possible values.
– Ted
Nov 13 '18 at 9:06
Precisely, no: golang.org/src/math/rand/rand.go?s=5359:5391#L168 doesn't return all possible values.
– Ted
Nov 13 '18 at 9:06
From the source comment: "Instead of that, if we round up to 1, just try again. Getting 1 only happens 1/2⁵³ of the time, so most clients will not observe it anyway.". They say they taken into account this.
– Vincenzo Maggio
Nov 13 '18 at 9:08
From the source comment: "Instead of that, if we round up to 1, just try again. Getting 1 only happens 1/2⁵³ of the time, so most clients will not observe it anyway.". They say they taken into account this.
– Vincenzo Maggio
Nov 13 '18 at 9:08
That's not the problem. The problem is that this code only returns multiples of 2⁻⁵³. Not all floating-point numbers between 0 and 1 are multiples of 2⁻⁵³, so you don't return all of them this way.
– Ted
Nov 13 '18 at 9:10
That's not the problem. The problem is that this code only returns multiples of 2⁻⁵³. Not all floating-point numbers between 0 and 1 are multiples of 2⁻⁵³, so you don't return all of them this way.
– Ted
Nov 13 '18 at 9:10
Listen, here's a discussion with even Russ Cox itself intervining: github.com/golang/go/issues/12290 saying this is not a huge problem. Thanks for the downvote.
– Vincenzo Maggio
Nov 13 '18 at 9:14
Listen, here's a discussion with even Russ Cox itself intervining: github.com/golang/go/issues/12290 saying this is not a huge problem. Thanks for the downvote.
– Vincenzo Maggio
Nov 13 '18 at 9:14
I added a comment explaining why I have a good reason not to use the code from the standard library. Thanks for making me notice that the rationale for my question was unclear! =)
– Ted
Nov 13 '18 at 9:20
I added a comment explaining why I have a good reason not to use the code from the standard library. Thanks for making me notice that the rationale for my question was unclear! =)
– Ted
Nov 13 '18 at 9:20
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53277105%2fgenerate-uniformly-random-float-which-can-return-all-possible-values%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
rand.Float64()
is probably the closest in-built way of doing it golang.org/pkg/math/rand/#Rand.Float64– Carlos Gonzalez
Nov 13 '18 at 9:00
1
No,
rand.Float64()
does it the naive way and doesn't return every possible value. I edited the question to add a link.– Ted
Nov 13 '18 at 9:08
with the chance of getting 1 being 1/2⁵³ i don't think you need to worry about it, if you do worry about it the docs you linked under cincenzo's answer explain how to fix it. so you can implement rand float 64 yourself
– Pizza lord
Nov 13 '18 at 9:13
1
You probably have to implement this yourself. mumble.net/~campbell/2014/04/28 might be a starting point.
– Ralf Stubner
Nov 13 '18 at 9:13
1
The paper tells you what to generate: “A uniform distribution over D ∩ (0, 1) can be generated by independently sampling an exponent (from the geometric distribution with parameter .5) and a significand (by drawing a uniform string from 0, 1⁵²).” That omits discussion of subnormals. I suspect covering the normals suffices for their purposes, but completing the geometric distribution by duplicating the probability for the least normal exponent and the subnormal values would fit nicely. (The probability of each floating-point number in [0, 1) would be proportional to its ULP.)
– Eric Postpischil
Nov 15 '18 at 2:13