Speaking of these, I’ve also got a draft for a #[compact] attribute, to address the bitfields use case, but let’s do one thing at a time.
(Terminology note: what is usually called a ‘exception’, i.e. jumping to some dedicated error handler, the IEEE 754 standard calls a ‘trap’; while what the standard calls ‘exceptions’ are the actual events that cause it, which may as well be exposed in a status flag register. I’ll be using this nomenclature here.)
I know that the x87 FPU (since the earliest 8087s) provides a ‘status word’ register, which contains flags that are set whenever overflow, underflow or other exception happens. So, testing for some of these outputs can be as cheap as checking a flag; I’m not very familiar with SSE or other architectures, though. I’m also a bit worried that relying on traps or reprogramming the FPU (like changing the 8087 control word) might run into problems at the FFI boundary, where foreign code may expect FPU to maintain its configuration bits, or where Rust code may fail to be given ownership of the trap handlers.
I don’t expect that to work even with built-in float types: Finite isn’t meant to provide new like that, that should have been a try_from. What’s the difference between Finite<BigFloat>::new(BigFloat::INFINITY) and Finite<f64>::new(f64::INFINITY)?
The design I had in mind here was that eventually the Ieee754 trait could be made to contain an associated constant (say, Ieee754::SIGNIFICAND_BITS), from which the compiler would be able to compute the memory layout of the type and determine which bit patterns are valid, based on the assumption that the type indeed represents an IEEE-type binary float (sign bit, biased exponent with the highest value reserved for infinities/NaNs, significand). I was thinking of user code that at one point uses f16 and f80 types provided by a crate, but then the compiler starts providing such types on their own; user code could then switch to the built-in ones with no hassle. Perhaps I should have written all this down. (And perhaps I should have named the trait Ieee754Binary; who knows, maybe Rust will have decimal floats at some point as well.)
I’m not sure how valuable this functionality would be; maybe we can do without it. I’ll be fine with making the trait unstable indefinitely, maybe #[fundamental] as well.
NaN stands for ‘not-a-number’, so NaNaN would be ‘not-a-not-a-number’. All this musing about NaNs made me think of Gary Bernhardt’s ‘wat’ talk, and I was also wondering how much sense it would make to use these wrappers on one another, and whether it would inevitably lead to type Batman = NaNaN<NaNaN<NaNaN<NaNaN<NaNaN<NaNaN<NaNaN<f64>>>>>>>;… just felt like being silly. (Not sure if you noticed the branch name…)
I’m not terribly attached to these names, they are mostly placeholders. In fact, UniqNaN is slightly misleading (it reserves two NaN payloads and the sign bit, so there are four valid NaN bit patterns for this type). If you can come up with better ones, be my guest.