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.
|
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.
rand.Float64()is probably the closest in-built way of doing it golang.org/pkg/math/rand/#Rand.Float64
– Carlos Gonzalez
Nov 13 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 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 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 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 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.
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.
edited Nov 14 at 7:33
asked Nov 13 at 8:52
Ted
428414
428414
rand.Float64()is probably the closest in-built way of doing it golang.org/pkg/math/rand/#Rand.Float64
– Carlos Gonzalez
Nov 13 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 at 7:31
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 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 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 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 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 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 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 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 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 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 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 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 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 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 at 9:42
gammatester
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 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 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 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 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 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 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 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 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 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 at 7:31
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 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 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 at 7:31
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 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 at 17:37
Ted
428414
428414
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 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 at 7:31
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 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 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 at 7:31
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 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 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 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 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 at 7:31
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 at 16:27
just updated my answer with another useful link
– Severin Pappadeux
Dec 3 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 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 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 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 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 at 16:27
answered Nov 13 at 14:30
Severin Pappadeux
9,36121432
9,36121432
(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 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 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 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 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 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 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 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 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 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 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 at 20:37
answered Nov 13 at 18:12
maerics
103k28200247
103k28200247
Thanks — but this generates a uniform distribution over the possible elements, not over the real interval. I clarified the question.
– Ted
Nov 14 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 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 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 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 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 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 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 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 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 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 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 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 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 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 at 9:02
Vincenzo Maggio
3,1051939
3,1051939
Precisely, no: golang.org/src/math/rand/rand.go?s=5359:5391#L168 doesn't return all possible values.
– Ted
Nov 13 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- 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 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 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 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 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 at 2:13