Edit: Ignore this, I'm wrong.
Edit: Ignore this, I'm wrong.
const std=@import("std");
test "IEEE fp representation" {
std.debug.print("\n{x}\n", .{@bitCast(u64, @as(f64, 1.0))});
std.debug.print("\n{x}\n", .{@bitCast(u64, @as(f64, 2.0))});
}
gives 3ff0000000000000
4000000000000000
note that the fraction parts for both 1.0 and 2.0 are entirely zero, as they are powers of two times [1].00000000...The "hidden bit", while tricky and hard to understand, is probably one of the few truly good decisions that IEEE-754 comittee made out of a lot of kludgey compromises. Note that if they had done it the way you suggest, there would be degenerate representations for the same number, leading to a much smaller total possible representations for the same bitspace (this is a minor problem for most object-based "Decimal" number systems used for financial Txs -- you often need to use a specialized equality function instead of the language builtin), the "hidden one" ensures that every binary form has a uniquely represented value, not counting craziness with NaNs, and plus/minus zero, sigh.
My brain fart was forgetting how floats work, seeing that the integer-resolution range for both single and double precision floats was exactly the number of bits in the fraction + the sign bit and then pattern matching.
Set the exponent to 0 and stuff in bits for the significand. This gets you a random float in the range [1,2). Now subtract 1.0.
Note that this misses many floating point values that should be selectable if you wanted all floats between 1 and 2, but it was good enough for video games.
It's worth pointing out that IEEE 754-2008 has decimal numbers like you describe without the hidden bit. But for decimals this can be really useful because numbers "remember" their exponent. So if you're doing addition of dollar values, for example, .84 + .12 = 1.00 rather than 1 (i.e. it remembers its significant digits).