How many floating-point numbers are in the interval [0,1]? (2017)

by burntcaramelon 4/15/23, 10:04 AMwith 66 comments
by cormacrelfon 4/15/23, 2:31 PM

For 32 bit floats, you can skip the math and just test all of them. LLVM will vectorise and unroll this nicely.

    fn main() {
        let start = std::time::Instant::now();
        let total = (0..=u32::MAX)
            .filter(|&x| {
                let f = f32::from_bits(x);
                0. <= f && f <= 1.
            })
            .count();
        println!("total {total} in {:?}", start.elapsed());
    }

    total 1065353218 in 1.364751583s
Edit: Apparently if you move the sum to its own function it runs in 500ms. A bit temperamental.

Edit 2: it's the size of the sum accumulator that makes it slow. The version above is like `.fold(0usize, |a, _| a + 1)`. When I moved it to another function, I cast the return value to u32, so LLVM saw basically `.fold(0u32, |a, _| a + 1)` and could use u32 throughout. Godbolt says the usize version ends up with floats in xmm* registers on x86, which fit 4 32-bit floats, but the u32 version ends up with floats in ymm* registers (8 32-bit floats) and similar half-as-wide behaviour on ARM.

by dr_dshivon 4/15/23, 2:31 PM

> 1,056,964,610. There are 4,294,967,296 possible 32-bit words, so about a quarter of them are in the interval [0,1]. Isn’t that interesting? Of all the float-pointing point numbers your computer can represent, a quarter of them lie in [0,1]. By extension, half of the floating-point numbers are in the interval [-1,1].

by an1sotropyon 4/15/23, 8:05 PM

Others have previously pondered this, and the related issue of how to randomly select floats in [0,1). I found this to be a helpful account:

https://mumble.net/~campbell/2014/04/28/uniform-random-float

and there's working C code too:

http://mumble.net/~campbell/2014/04/28/random_real.c

by mike_hockon 4/15/23, 2:14 PM

float32: 1 for exactly 1.0 (exponent=127,mantissa=0) + 127 * 2^23 for [0,1) (127 distinct exponent values (0 through 126) and any bit pattern in the mantissa; this includes +0 and denormals) + 1 for negative zero = 1065353218 = 0x3f800002 + 2, i.e. two more than the representation of 1.0, because everything up to and including that (as an unsigned integer) represents something in [0,1], plus the missing negative zero.

by EthicalSimilaron 4/15/23, 2:23 PM

I apply the above theory for generating random numbers in JavaScript, based on given inputs (server seed, client seed, and nonce). JavaScript uses double precision floating point numbers, meaning 52 bits for the mantissa.

  const hash = crypto
    .createHmac('SHA256', serverSeed)
    .update(`${ clientSeed }:${ nonce }`)
    .digest('hex');
  
  const significant = hash.substring(0, 52 / 4);
  const integer = parseInt(significant, 16);
  const float = integer / (2 * 52);
  
  return { float, hash, };

by macintuxon 4/15/23, 3:09 PM

Presumably an offshoot of the discussion around floating point gotchas in gaming: https://news.ycombinator.com/item?id=35539595

by nighthawk454on 4/15/23, 8:24 PM

Tangentially, it may be interesting to think how this differs for other float formats. Such as: Google Brain's bfloat16, Nvidia's TensorFloat

by dangon 4/15/23, 11:44 PM

Discussed at the time:

How many floating-point numbers are in the interval [0,1]? - https://news.ycombinator.com/item?id=13759458 - Feb 2017 (153 comments)

by pestatijeon 4/15/23, 8:02 PM

So is it 1,056,964,610 (TFA)

or is it 1,065,353,218 (comments) and why the difference?

by eimrineon 4/15/23, 3:58 PM

Can I see a graph to know how many of them are in any interval?

by garbagecoderon 4/15/23, 6:50 PM

As many as you want depending on how many bits. Floats are made to approximate real numbers. There is an uncountably infinite number of real numbers on any open interval of real numbers. Since (0,1)\in[0,1] there you go. Just keep adding bits.

by sampoon 4/15/23, 4:28 PM

About 1/4 of all of them.

by tand22on 4/15/23, 4:15 PM

A grey link already for me xD