Skip to content
mikwielgusPublic

About

Numeric traits for generic mathematics. More fine-grained, less restrictive and less conservative alternative to num-traits.

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Latest commit

 

History

202 Commits

Folders and files

Repository files navigation

Repository Docs Crates.io MSRV MIT OR Apache 2.0

numlike

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.

Usage

Adding dependency

[dependencies]
numlike = { version = "0.8.0" }

Comparison to other libraries

Why numlike and not num-traits?

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 &self instead of self. 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.

    • numlike does not have this problem -- its trait functions always accept arguments by value, so there's no unnecessary cloning.
  • num-traits's Zero and One traits require Add and Mul traits, 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-traits also requires Output = Self (i.e. operation must have closure property) for Add and Mul, making it impossible to use Zero and One for statically-typed unit of measurement libraries like uom, where, for instance, multiplying two lengths gives you area, which is a different dimension.
      • numlike does not have these problems because it does not have any supertraits for its Zero and One.
  • num-traits's Bounded trait only returns finite minimum and maximum values. This makes no difference for integers, but matters for floats. For instance, for f32, .max_value() returns f32::MAX, which is actually the largest finite number, equal to 3.40282347e+38, not the positive infinity. num-traits has no interface to generically obtain negative or positive infinity as the min. or max. value.

    • numlike solves that by providing MinExtended::MIN_EXTENDED/ MaxExtended::MAX_EXTENDED trait constants that are negative and positive infinities for floats, and MinFinite::MIN_FINITE/ MaxFinite::MAX_FINITE trait constants that are always finite values just as above num-traits's Bounded does.

      MIN and MAX names, 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 terms MIN and MAX to actually mean MIN_FINITE and MAX_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 leave MIN and MAX identifiers 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 of Signed trait, 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 also Rem, Zero, and One, 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 numlike provides these methods through two fine-grained, decoupled traits, Sgn and Abs, 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.
  • num-traits does not provide checked mathematical operation traits, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedNeg, CheckedRem, for floats.

    • numlike implements its own versions of these traits for all numeric primitives.
  • Developers of num-traits do 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.
  • Furthermore, numlike also 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::cmp traits instead, consider using ordered-float crate's OrderedFloat float type wrapper instead. This is obviously often the better choice, because core::cmp traits will work with a much larger number of existing libraries and have Rust's syntactic sugar.

About

Numeric traits for generic mathematics. More fine-grained, less restrictive and less conservative alternative to num-traits.

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages