Numeric traits for generic mathematics. More fine-grained,
less restrictive and less conservative alternative to
num-traits.
This crate has no unsafe code and has only one mandatory dependency, pastey,
and is no_std-compatible. Most of no_std operations on floating point
numbers still have a dependency on libm,
which is gated behind libm feature flag. MSRV is 1.85.
If you are looking for abstract container traits in addition to
abstract number traits, also check out another crate of ours,
maplike.
[dependencies]
numlike = { version = "0.8.0" }We developed numlike primarily because we disagree with many of design
decisions in the venerable num-traits crate.
-
A large number of
num-traits's trait functions accept immutable reference, not value, as input. For example,checked_add(),.abs(),.signum(),.to_ne_bytes(),.from_be_bytes()(and many more) all take in&selfinstead ofself. This forces the implementors to needlessly clone the value each time any of these operations is performed, which degrades performance for arbitrary-precision numbers of non-trivial size.numlikedoes not have this problem -- its trait functions always accept arguments by value, so there's no unnecessary cloning.
-
num-traits'sZeroandOnetraits requireAddandMultraits, respectively, to be implemented.This makes it impossible to distinguish a 0 for algebraic structures that don't implement addition. For example, absorption magma (magma with zero) and absorption monoid (monoid with zero) are usually described without addition, yet still with an element denoted as 0, the absorbing element (or just absorber).
Likewise, there are reasons to distinguish a 1 when there is no multiplication. For example, a complete multiplication implementation for all elements could be inefficient, or 1 may be supposed to designate merely the generating element (aka. the generator).
num-traitsalso requiresOutput = Self(i.e. operation must have closure property) forAddandMul, making it impossible to useZeroandOnefor statically-typed unit of measurement libraries likeuom, where, for instance, multiplying two lengths gives you area, which is a different dimension.numlikedoes not have these problems because it does not have any supertraits for itsZeroandOne.
-
num-traits'sBoundedtrait only returns finite minimum and maximum values. This makes no difference for integers, but matters for floats. For instance, forf32,.max_value()returnsf32::MAX, which is actually the largest finite number, equal to3.40282347e+38, not the positive infinity.num-traitshas no interface to generically obtain negative or positive infinity as the min. or max. value.-
numlikesolves that by providingMinExtended::MIN_EXTENDED/MaxExtended::MAX_EXTENDEDtrait constants that are negative and positive infinities for floats, andMinFinite::MIN_FINITE/MaxFinite::MAX_FINITEtrait constants that are always finite values just as abovenum-traits'sBoundeddoes.MINandMAXnames, without suffixes, are intentionally not used because floats do not have minimum and maximum values in the strict mathematical sense due to the presence of NaN values, which are unordered. And Rust uses the termsMINandMAXto actually meanMIN_FINITEandMAX_FINITE, thus omitting positive and negative infinity, that are nevertheless mathematically valid elements of the set, which mixes up terminology even more. We prefer to use terms in their strict mathematical sense to avoid confusion, so we chose to just leaveMINandMAXidentifiers unused.
-
-
In
num-traits, if you want to calculate absolute value (.abs()), the signum function (.signum()or.sgn()), or to check whether a number is positive or negative, you need to have a bound ofSignedtrait, which is not implemented on unsigned integer types.What's even worse, that trait requires your type to implement all the field operation traits (
Add,Sub,Mul,Div), and alsoRem,Zero, andOne, and some more, thus precluding its use on more abstract (e.g. group-like) mathematical structures.- But having these methods generically for both signed and unsigned types
can be useful for finding greatest common divisors, finding canonical
denominators, reducing fractions, combining and simplifying radicals, so
numlikeprovides these methods through two fine-grained, decoupled traits,SgnandAbs, implemented for all numeric primitives, not only signeds. These traits do not have any other bounds, so they can be easily implemented for any algebraic structure without the need for any additional assumptions.
- But having these methods generically for both signed and unsigned types
can be useful for finding greatest common divisors, finding canonical
denominators, reducing fractions, combining and simplifying radicals, so
-
num-traitsdoes not provide checked mathematical operation traits,CheckedAdd,CheckedSub,CheckedMul,CheckedDiv,CheckedNeg,CheckedRem, for floats.numlikeimplements its own versions of these traits for all numeric primitives.
-
Developers of
num-traitsdo not want to make breaking changes. This provides stability for a very large amount of dependents, but prevents the above issues and other ones from being solved.- Because of that, we have decided to roll our own library (this crate).
However, because it's in active development, we are lacking the stability of
num-traits-- we are more likely to have breaking changes and bugs.
- Because of that, we have decided to roll our own library (this crate).
However, because it's in active development, we are lacking the stability of
-
Furthermore,
numlikealso has its own features, such as:- Equality and order traits that fix
NaNs to be the highest value in the set, even larger than positive infinity, allowing for total order:NanfixPartialEq,NanfixEq,NanmaxPartialOrd,NanmaxOrd,NanminPartialOrd,NanminOrd.- But if you want
core::cmptraits instead, consider usingordered-floatcrate'sOrderedFloatfloat type wrapper instead. This is obviously often the better choice, becausecore::cmptraits will work with a much larger number of existing libraries and have Rust's syntactic sugar.
- But if you want
- Equality and order traits that fix