-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Redesign and document the traits/functions related to numbers #4819
Comments
Also related to #1284 (Complex Numbers), which should be taken into consideration. Other types that may not fit in core but should fit into the trait system
|
Good call on making a new bug. Looking forward to seeing progress on this front - it will be of great use to maths libraries. |
I made a bikeshed, https://github.com/mozilla/rust/wiki/Bikeshed-Numeric-Traits, hoping that it can grow into actual documentation at some point describing the thoughts going into the system. Feel free to add comments, fix bugs &c. |
According to @catamorphism and @nikomatsakis (in this comment in #4183), we currently have a blockage at #4678, pushed back until the 0.7 milestone. We might have to figure out some workarounds for the time being and patch things up later once the issues are resolved. |
Yeah, waiting for 0.7+ seems like a no-go for me too, I think we'll have to start without the operator traits and try to make sure the fix is in as early as possible for 0.7. |
This restores the trait that was lost in 216e85f. `Num` will eventually be broken up into a more fine-grained trait hierarchy in the future once a design can be agreed upon. To contribute to the discussion about the future of Rust's numeric traits, please see issue #4819 and the [wiki page](https://github.com/mozilla/rust/wiki/Bikeshed-Numeric-Traits). I have also switched to [implementing NumCast using macros](brendanzab@353ce87). This removes a significant number of lines of code.
This renaming, proposed in the [Numeric Bikeshed](https://github.com/mozilla/rust/wiki/Bikeshed-Numeric-Traits#rename-modulo-into-rem-or-remainder-in-traits-and-docs), will allow us to implement div and and modulo methods that follow the conventional mathematical definitions for negative numbers without altering the definitions of the operators (and confusing systems programmers). Here is a useful answer on StackOverflow that explains the difference between `div`/`mod` and `quot`/`rem` in Haskell: (When is the difference between quotRem and divMod useful?)[http://stackoverflow.com/a/339823/679485]. This is part of the numeric trait reforms tracked in issue #4819.
As part of the numeric trait reform (see issue #4819), I have added the following traits to `core::num` and implemented them for the appropriate types: ~~~rust pub trait Signed: Num + Neg<Self> { fn abs(&self) -> Self; fn signum(&self) -> Self; fn is_positive(&self) -> bool; fn is_negative(&self) -> bool; } pub trait Unsigned: Num {} pub trait Natural: Num + Ord + Quot<Self,Self> + Rem<Self,Self> { fn div(&self, other: Self) -> Self; fn modulo(&self, other: Self) -> Self; fn div_mod(&self, other: Self) -> (Self,Self); fn quot_rem(&self, other: Self) -> (Self,Self); fn gcd(&self, other: Self) -> Self; fn lcm(&self, other: Self) -> Self; fn divisible_by(&self, other: Self) -> bool; fn is_even(&self) -> bool; fn is_odd(&self) -> bool; } ~~~ I have not implemented `Natural` for `BigInt` and `BigUInt` because they're a little over my head. Help with this would be most appreciated.
�Was happy to see |
I think you're right, it is a lot more clear with the longer names. I updated the bikeshed. |
I'll implement |
If we're going for 'self-documenting', I wonder if either |
Population count is self-documenting in my opinion, and it's the domain term for it. Python has a method called bit_count and it does something completely different (counts the bits needed to represent the number - including zeroes). |
Ahh ok. If it's a domain term that's fine. |
As part of the numeric trait reform (see issue #4819), I have added the following traits to `core::num` and implemented them for floating point types: ~~~rust pub trait Round { fn floor(&self) -> Self; fn ceil(&self) -> Self; fn round(&self) -> Self; fn trunc(&self) -> Self; fn fract(&self) -> Self; } pub trait Fractional: Num + Ord + Round + Quot<Self,Self> { fn recip(&self) -> Self; } pub trait Real: Signed + Fractional { // Common Constants fn pi() -> Self; fn two_pi() -> Self; fn frac_pi_2() -> Self; fn frac_pi_3() -> Self; fn frac_pi_4() -> Self; fn frac_pi_6() -> Self; fn frac_pi_8() -> Self; fn frac_1_pi() -> Self; fn frac_2_pi() -> Self; fn frac_2_sqrtpi() -> Self; fn sqrt2() -> Self; fn frac_1_sqrt2() -> Self; fn e() -> Self; fn log2_e() -> Self; fn log10_e() -> Self; fn log_2() -> Self; fn log_10() -> Self; // Exponential functions fn pow(&self, n: Self) -> Self; fn exp(&self) -> Self; fn exp2(&self) -> Self; fn expm1(&self) -> Self; fn ldexp(&self, n: int) -> Self; fn log(&self) -> Self; fn log2(&self) -> Self; fn log10(&self) -> Self; fn log_radix(&self) -> Self; fn ilog_radix(&self) -> int; fn sqrt(&self) -> Self; fn rsqrt(&self) -> Self; fn cbrt(&self) -> Self; // Angular conversions fn to_degrees(&self) -> Self; fn to_radians(&self) -> Self; // Triganomic functions fn hypot(&self, other: Self) -> Self; fn sin(&self) -> Self; fn cos(&self) -> Self; fn tan(&self) -> Self; // Inverse triganomic functions fn asin(&self) -> Self; fn acos(&self) -> Self; fn atan(&self) -> Self; fn atan2(&self, other: Self) -> Self; // Hyperbolic triganomic functions fn sinh(&self) -> Self; fn cosh(&self) -> Self; fn tanh(&self) -> Self; } /// Methods that are harder to implement and not commonly used. pub trait RealExt: Real { // Gamma functions fn lgamma(&self) -> (int, Self); fn tgamma(&self) -> Self; // Bessel functions fn j0(&self) -> Self; fn j1(&self) -> Self; fn jn(&self, n: int) -> Self; fn y0(&self) -> Self; fn y1(&self) -> Self; fn yn(&self, n: int) -> Self; } ~~~ The constants in `Real` could be [associated items](http://smallcultfollowing.com/babysteps/blog/2013/04/03/associated-items-continued/) in the future (see issue #5527). At the moment I have left the constants in `{float|f32|f64}::consts` in case folks need to access these at compile time. There are also instances of `int` in `Real` and `RealExt`. In the future these could be replaced with an associated `INTEGER` type on `Real`. `Natural` has also been renamed to `Integer`. This is because `Natural` normally means 'positive integer' in mathematics. It is therefore strange to implement it on signed integer types. `Integer` is probably a better choice. I have also switched some of the `Integer` methods to take borrowed pointers as arguments. This brings them in line with the `Quot` and `Rem` traits, and is be better for large Integer types like `BigInt` and `BigUint` because they don't need to be copied unnecessarily. There has also been considerable discussion on the mailing list and IRC about the renaming of the `Div` and `Modulo` traits to `Quot` and `Rem`. Depending on the outcome of these discussions they might be renamed again.
Does rust use the same floating point constants as the C99 standard (see section 5.2.4.2.2, item 14)? I notice that the double values are the same as those defined in |
If the constants are not the same for f32/f64 then that would be a bug. The ones used in C headers are crafted to give correctly rounded values. |
@JensNockert So the various min/max values (including _exp, _10_exp) should be the same for f64/f32? |
I would like to discuss some minor issues I see with the Signed trait here. Currently it is defined as
All of the functions in this trait can also be implemented for 'unsigned' types considering that unsigned actually means non negative to us. Furthermore abs is usually understood to be the L2 norm and is defined for normed vector space and we probably want to define it for complex numbers but skip the rest of this trait. Also the most important function specific to signed types is the additive inverse, which gets implemented as Neg which is part of the Num trait. I think it would be better to have a Normed trait containing abs and maybe abs_sub, putting signum, is_positive and is_negative into the Num trait and making the Singed trait contain Neg and maybe a copysign function as in C/C++. |
@Casorati putting (I agree about |
@huonw The integer types (both the singed and unsigned ones) are already modulo rings so I don't see any problem there. In practice one either chooses to represent them by numbers in a range [0, 2a[ or [-a, a[ and defines the sign to be the sign of the representing element in that range. Polynomials are vector spaces and how often have you seen algorithms which are expected to work for both int32 and polynomials at the same time? I think complex numbers are much more relevant to this discussion not only because they define a very special vector space but also because the complex type in part of the C99 standard. If we want them to implement the trait Num, then you are right, signum functions must be put elsewhere. How about this:
|
I know, but they are rarely used for their modulo-ring properties; they are just pretending to be ℤ and ℕ (also, strictly speaking, signed overflow is undefined behaviour (in C, not 100% sure about LLVM), not wrap around, so the signed variants aren't necessarily/always modulo rings).
This isn't about algorithms for both (although Euclid's algorithm is one example); it's for having to coerce polynomials into a
I'm not sure I understand this, but currently we have Those changes sound ok to me; but as I said before, we'd need something like trait Normed<Base> {
fn abs(&self) -> Base;
fn abs_sub(&self, other: &Self) -> Self;
} for |
(Also, I guess |
What do you think about generic implementations like
Although it would be nice to have a branchless implementation for |
std::int::pow seems to be have been left behind in all of this. Should it be made an associated function or a part of a trait? |
Hey folks. I'm working on a syntax extension that will auto-generate
As opposed to Open questions from IRC:
|
I'd guess a |
This PR solves one of the pain points with c-style enums. Simplifies writing a fn to convert from an int/uint to an enum. It does this through a `#[deriving(FromPrimitive)]` syntax extension. Before this is committed though, we need to discuss if `ToPrimitive`/`FromPrimitive` has the right design (cc #4819). I've changed all the `.to_int()` and `from_int()` style functions to return `Option<int>` so we can handle partial functions. For this PR though only enums and `extra::num::bigint::*` take advantage of returning None for unrepresentable values. In the long run it'd be better if `i64.to_i8()` returned `None` if the value was too large, but I'll save this for a future PR. Closes #3868.
Closing in favor of #10387. The recent trend has been to reduce the number of traits in the |
Thanks! This has been a great journey. I think we have have all learned a great deal and are finally converging on the best design we can considering the challenges. Hopefully we can avoid the problems that the Haskell98 and Scala libs have faced as our library ecosystem evolves. |
In #4789 discussion was started about redesigning the traits related to numbers and I think the first step is to design and document the traits we think are required and why.
We probably also depend on #4183 and some other bugs for the actual implementation?
(I have an initial sketch/RFC here, based on the work in #4789 etc. https://gist.github.com/JensNockert/4719287)
The text was updated successfully, but these errors were encountered: