Floating point fun: iterating the number space

As a joke the other day I posted a code snippet on Mastodon:

for (var i = -Infinity; i < Infinity; i++) {
  /* this should cover all possibilities */
}

Such an infinite loop is not possible, as adding 1 to -Infinity just gives you -Infinity again, and you never get anywhere. ;)

But even if you started from the minimum representable individual number in a double-precision floating point you wouldn’t actually iterate anywhere because there’s not enough precision to represent the added 1.

Which got me thinking, how *would* you iterate over the entire set of floating point numbers between -Infinity to +Infinity, knowing that the intervals between the numbers are variable?

Well if you want to get everything, including non-integral values, you can iterate over the available range of exponents and fractional values, and either bit-shift them together manually (ick!) or multiply/add/pow them together in floating point land.

Something like this:

function iterate_all_floats(callback) {
  // exponent is additively biased in the binary rep,
  // so we go from negative to positive vals.
  let min_exp = -(Math.pow(2, 10) - 2); // all binary 1s are reserved
  let max_exp = Math.pow(2, 10) - 1;

  let min_frac = 0;
  let max_frac = Math.pow(2, 52) - 1;
  let frac_divisor = Math.pow(2, 52);

  let min_sub = 0;
  let max_sub = Math.pow(2, 52) - 1;
  let sub_divisor = Math.pow(2, 52);

  callback(-Infinity);

  // Start from the biggest negative numbers and go up towards -0
  for (let exp = max_exp; exp >= min_exp; exp--) {
    for (let frac = max_frac; frac >= min_frac; frac--) {
      callback(-(1.0 + frac / frac_divisor) * Math.pow(2, exp));
    }
  }

  // Negative subnormals and -0
  for (let sub = max_sub; sub >= min_sub; sub--) {
    callback(-(sub / sub_divisor) * Math.pow(2, -1022));
  }

  // +0 and positive subnormals
  for (let sub = min_sub; sub <= max_sub; sub++) {
    callback((sub / sub_divisor) * Math.pow(2, -1022));
  }

  // Start from just past +0 and move up to the biggest numbers
  for (let exp = max_exp; exp <= max_exp; exp++) {
    for (let frac = min_frac; frac <= max_frac; frac++) {
      callback((1.0 + frac / frac_divisor) * Math.pow(2, exp));
    }
  }

  callback(Infinity);
}

iterate_all_floats(function(f) {
  // Convert to binary for display
  let floats = new Float64Array(1);
  let bytes = new Uint8Array(floats.buffer);
  let str = '';
  floats[0] = f;
  for (let i = 3; i >= 0; i--) {
    let b = bytes[i];
    for (let bit = 7; bit >= 0; bit--) {
      let val = (b >> bit) & 1;
      str += val;
    }
  }
  console.log(str, f);
});

Enjoy!