Hi!
I'd like to meet and talk about the status of floating point traits in Rust. This is just one topic out of many that are related to numbers, and I hope we can have more of these discussions soon!
This can touch upon all kinds of mathematics, game dev, data sciences and machine learning and other interests, hopefully a lot of you want to participate in some way.
I would like to invite of course @cuviper and @hauleth that have taken up the mantles as num crate maintainers. (Maybe you are more, I don't know!)
I am going to toss in my idea first here. Please tear into it or disregard it. It's fine if we discuss meta-issues before we discuss concrete things. Is it, for example, a fool's errand to try to make different kinds of projects use the same base traits? We do have something to win if we make a better trait, don't we?
Goal
- I want to write code once, that works for both f32 and f64 (Float)
- I want to write code once, that works for all of f32, f64, Complex
<
f32>, Complex<
f64> (ComplexFloat) - I want to be able to special case for each type when it is needed.
- I want to express myself using regular Rust syntax like
+
,+=
, and so on as much as possible.
Design
With a narrow type requirement, we can provide a lot of features.
Examples:
a. Any type that defines addition => we can provide only very rudimentary features
b. Any type that is a primitive floating point type => we can provide a lot of the features they have in common
(1) A float trait should include all the usual floating point operations, and more. I want to be greedy here so that when we have a narrow type requirement (Float), there should be no arbitrary roadblocks. Primitive floating point types implement Display and are 'static, so those should be properties of the trait. Primitive floating point types are Send + Sync, so those should be properties of the trait, so I can send work to a thread without fuss, and so on. ⁵
(2) ComplexFloat should work just like Float: write code once that works for all the four types. It for example allows expressing a complex inner product using conjugation, and for the real numbers, conjugation is simply the identity function.
(3) will be solved by specialization, but it is also solved by the traits by themselves, with special case methods like ComplexFloat::is_complex() -> bool
. When you need it, it should be simple to conditionally do an operation.
(4) is addressed by making sure operator traits are included in traits (1) and (2) as far as possible. We can't directly expose the as
operator; it is a core Rust feature, but it was not properly available in generic code. Primitive numeric types can be cast with as
, so it should be a feature you can use through Float too. I have a draft trait AsPrim for that.
Draft Traits
Prior Art
⁵ We can't even list all the add-on capabilities that f32, f64 have, those that come with each extra crate you add to your project, for example serializable, randomizable, et cetera. Cargo support for opportunistic conditional compilation would do well here, so that a numerics crate can include serialization support only when it's already being used in the project.