TransWikia.com

Why won't std::uniform_real_distribution generate proper floating values?

Stack Overflow Asked on November 27, 2021

I’m trying to print random float(32-bit) values. I tried using uniform_real_distribution for this purpose. I wrote the following code,

int main()
{
  std::random_device rd{};
  std::mt19937 gen{rd()};

  std::uniform_real_distribution<float> dist(-1e18,1e18);

  float random_val = dist(gen);

  printf("%.20fn", random_val);

  return 0;
}

Now, the output is strange. All I get is very big numbers (always near the upper or lower bound) with no fractions. Following are some of the outputs I saw,

-149399166081040384.00000000000000000000
128349565723082752.00000000000000000000
-323890424458510336.00000000000000000000
802221481969844224.00000000000000000000
817395979383734272.00000000000000000000

They are always like these, and it doesn’t matter if I change the bounds. What is wrong here?

2 Answers

The reason the numbers have no fractional digits is because 32-bit floating-point can't store a number that big with fractional digits. You only get about 7 decimal digits of precision with a 32-bit float. So any number larger than 1e7 will have digits past the 7th digit that are, at best, unreliably valued and ultimately meaningless.

In your case, that just happens to be a bunch of zeros.

The reason your numbers skew towards being big is that most numbers on the range [-1e18, 1e18) are big. The set of numbers on the range [-1, 1) is only 10% (approximately) of the numbers within the range [-10, 10). Which itself is only 10% (approximately) of the numbers on the range [-100, 100). Etc. So the chance of getting a even a 5 digit number on the range [-1e18, 1e18) is at lottery-winning odds.

Remember: the distribution attempts to uniformly select within the range randomly, not to generate random 32-bits that correspond to a valid float.

Answered by Nicol Bolas on November 27, 2021

float is usually IEEE Single-precision floating-point format, which works like scientific notation, which has 1 sign bit, 8 exponent bits, and 23+1 fraction bits. So 817395979383734272.0 is stored as 1.41795599 * 2^59 in memory. That fractional part is only ~8 decimal digits, because that's all the precision it can squeeze into that ~24 bits. Mark Ransom reminds me that as a result, all floats greater than ~100,000,000 will be integers, simply becauase they're's not enough bits to also store any fractional parts.

Since there's ~24 bits for the fractional part, that means it can hold 7.2 decimal digits of precision. So the first 7 decimal digits are accurate, the 8th decimal digit is semi-accurate, and subsequent decimal digits are practically random when rendering float as text.

817395979383734272.0
^      ^^          
|      |basically random
|      semi-accurate
accurate

For double (using IEEE Double-precision floating-point format), it uses 1 sign bit, 11 exponent bits, and 52+1 fraction bits. This stores 15.9 decimal digits accurately, so can still hold fractional parts until values are greater than ~1,000,000,000,000,000.

David Shwartz also points out that it's common to assume that random floats will have some small numbers and some large numbers, but mathematically, almost all uniformly randomly generated floats will be within two magnitudes of the maximum. In your case, that's >1e16 and <-1e16. This is mathematically correct, but can also factor into your confusion.

Answered by Mooing Duck on November 27, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP