num-traits
num-traits copied to clipboard
Float should be split into smaller traits
Right now Float trait contains methods min_value, max_value, floor, ceil, round, trunc, fract, abs and signum that are also applicable to fixed point numbers. Separating these methods into one or more traits would allow implementing those sub-traits for fixed crate numbers, which implements fixed-point numbers.
On a related note, I was implementing some num-traits in a branch of the fixed crate in order to fulfill a feature request. I was going to implement Signed and Unsigned when appropriate, but they both depend on Num which requires a from_str_radix method. Now in the fixed crate, parsing of the useful radix values 2, 8, 10 and 16 is implemented, but not others, so I could not implement Signed or Unsigned. (Radix values 2, 8, 16 are easy to parse, and 10 has a factor of 2 which makes it easier than other radix values as it puts a limit on the number of fractional digits that might need to be parsed.)
We have Float, then Real drops a few methods related to NaN/infinity, and FloatCore drops mostly transcendental methods that need external support (libm). Maybe we could also add RealCore as a subset of FloatCore, but I'm loath to break this up too much. We can't be everything to everyone.
I also don't want to make any breaking changes here. These traits are used in the public API of a lot of crates, so it would be very disruptive to bump semver. We're effectively stable, despite the pre-1.0 version.
We're effectively stable, despite the pre-1.0 version.
On that note, maybe I should go ahead and release 1.0 and use the semver trick to have 0.2.x re-export that, so they remain compatible. (Same as I did for 0.1 to 0.2.) If someday we do decide to redesign things, there's always 2.0 and beyond...
I do think that splitting Float into more granular pieces important. For example, crate euclid implements its own versions of Ceil, Floor, Round and even One and Zero traits, because Float is "too fat" and One/Zero in num_traits for some reason requires Add. Some notes from euclid code:
// Euclid has its own Zero and One traits instead of of using the num_traits equivalents.
// Unfortunately, num_traits::Zero requires Add, which opens a bag of sad things:
// - Most importantly, for Point2D to implement Zero it would need to implement Add<Self> which we
// don't want (we allow "Point + Vector" and "Vector + Vector" semantics and purposefully disallow
// "Point + Point".
// - Some operations that require, say, One and Div (for example Scale::inv) currently return a
// type parameterized over T::Output which is ambiguous with num_traits::One because it inherits
// Mul which also has an Output associated type. To fix it need to complicate type signatures
// by using <T as Trait>::Output which makes the code and documentation harder to read.
num_traits is the closest we have to stdlib, but for numeric traits. As a result I feel like it would be the perfect place to provide a set of common traits for other crates to use to enable interoperability.
While it's a little sad that euclid had to go its own way, it's not the end of the world.
num_traitsis the closest we have tostdlib, but for numeric traits. As a result I feel like it would be the perfect place to provide a set of common traits for other crates to use to enable interoperability.
In case you're not aware, that de facto status is actually pretty direct. The num crate was initially born from standard library code, and those contents removed from std during the stabilization phase before Rust 1.0. Later we split into the current sub-crates, and kept compatibility by having num re-export the same items. Ultimately, num's broad pseudo-standard usage is exactly why I don't want to risk a schism with new semver releases. I've done so for some of the type crates, like num-bigint, but traits are riskier.
So let's keep this issue on track, without breaking the current traits. Would a new RealCore subset of Real be useful to you? That would include all the methods you mentioned, basically FloatCore without NaN/infinity methods. We can make this implemented for all current T: FloatCore, just as we do on Real for T: Float.
I think the following traits should exist in num_trait in some shape or form:
- Fractional part manipulation:
floor,ceil,round,trunc,fract Bounded:min_value,max_value- already exists- Min-max:
min,max(this might not even be needed, asOrdhas those methods already) - Sign manipulation:
abs,signum - Trigonometry functions:
sin,cos, etc - Finite/infinite numbers:
is_nan,is_finite - Powers and logarithms:
powi,powf,sqrt,exp, etc
I'm mostly interested in the first 3 (1 of which already exists).
EDIT: Also it would be useful if requirements for Zero and One were relaxed (Add and Mul), but I'm not sure how realistic is to implement that without breaking other crates.
I vote for more flexible structure for float traits. Float is indeed too fat, even FloatCore is too fat to me. I understand that compatibility is important, but maybe the earlier we make these change, the less burden we will have in future. A good API is important for rust to be more user-friendly/dev-friendly, as rust doesn't support implementing foreign traits yet.
I think at least the float traits should be divided so that bigfloat types and simd types can implement some of them easily.