305. to_bits — Hash and Dedup Floats Without a Wrapper Crate
HashSet<f64> doesn’t compile — floats aren’t Hash or Eq. f64::to_bits turns each float into its exact u64 bit pattern, which is both.
Try to dedup a list of readings and the compiler stops you at the door:
| |
f64 can’t be Eq because NaN != NaN, and it can’t be Hash because hashing requires consistent equality. The usual escape hatch is a wrapper crate like ordered-float — but for keying and dedup, std already has what you need:
| |
to_bits is a transmute, not a cast: (1.0f64).to_bits() is 4607182418800017408, not 1. Every distinct float maps to a distinct u64, and f64::from_bits round-trips it losslessly:
| |
Two things bit-equality changes, both usually what you want for keys:
NaNbecomes equal to itself (same payload, same bits), so aNaNkey is stored once instead of leaking in forever.0.0and-0.0compare==as floats but have different bit patterns, so they count as two keys.
Note the first assert above: 0.1 + 0.2 stays in the set alongside 0.3 because they really are different floats. to_bits doesn’t paper over float imprecision — it makes it visible. If you want tolerance-based grouping, that’s a different tool; for exact identity — memoization keys, dedup, caching — the bit pattern is the honest answer.