-
Notifications
You must be signed in to change notification settings - Fork 20
Add associated consts to f32, f64 for mathematical constants #210
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
Comments
rust-lang/rfcs#2700 chose not to do this for the mathematical constants. So I think this ACP should address what's changed since then that means it was a bad idea then but a good idea now. And if moving One thing that comes to mind is that I agree with @joshtriplett that it would be nice to be able to |
Relevant tracking issue: rust-lang/rust#68490 I also suggested this there, but I think it's relevant to put here too. You can kind of emulate a module in this case by using Also possible with just an inherent constant, but doesn't result in purely path syntax: |
Thanks I didn't know that. Seems like it was decided that it could be left to a later RFC. I'd be happy to create it, if people want (although maybe that should be left to someone who has more experience creating RFCs), or wait until (if ever) it's possible to Personally, I would prefer to have the constants available even without the ability to use associated constants. I still think that the advantage of being able to type out As for |
First off, I do agree withs the points that having to write However, the mathematical constants are fundamentally different from the current associated constants, which are all either special values of that type like The constants in IMO, ideally we could do something like this:
That is, the named mathematical constants would have one definition each, independently of the numeric types,
The above would be equally correct for both f32 and f64, which is difficult to do with just the current definitions. A couple of other things to consider:
|
What is Anyways this definitely seems interesting but I'm not sure if it would happen in the forseeable future. And for now what might one day happen to PI is not really relevant to whether The ability to get the lower and upper bound seems cool, but I don't know of any language that has that in its standard library... perhaps it is best left for a crate.
Yeah, that's neat. Although it's worth mentioning the same could be said of NAN, INFINITY (although you can always do At least with this proposal it would be possible to do: type MySpecialType = f32; // might change to f64!
// ...
let pi = MySpecialType::PI; which is not currently possible (admittedly you can do And if for x in data {
if x.abs() <= (typeof x)::PI {
// ...
}
} :)
Yeah, that's a good point. I will add it to the "Drawbacks" section of the RFC.
Well, it is true that |
I've got another option for you. Put these constants on a trait: pub trait MathConsts {
const PI: Self;
// etc
} Only when the trait is in scope, you can access all the constants directly on use std::math::MathConsts;
dbg!(f64::PI); This approach doesn't clutter editor suggestions or the docs page. And you can even use type inference when you need to: for x in data {
if x.abs() <= MathConsts::PI {
// ...
}
} |
This would be especially nice since then you would be able to use PI in a generic context. Unfortunately it would mean that adding a new constant would be a breaking change for any types which impl MathConsts For what it's worth this trait already exists but with fns instead of consts in the I will add this to the "alternatives" section of the RFC though! |
I think it would be best to seal this trait so it's only implementable by std types anyways. Another option is to just create a supertrait for each addition and change the name resolution on an edition boundary. |
One way to do that would be to add // in std::num::consts
pub struct Pi; And add That way you'd have I played around with doing this for (Dunno if it's something people would want, but I think it does at least mean a simpler approach than adding new traits for every constant of interest.) |
This does seem to match mathematical reality most closely in that we cannot express real numbers exactly, so the idea of converting with From has considerable appeal. If we have a symbolic |
I think it would be best to just leave this up to a let n = Rational::new(2, 1) * Rational::from(Pi);
dbg!(n.sin()); // 0/1 |
These are actually a good example of why I mentioned lower bounds; your examples would not be strictly correct for both f32 and f64. I really did mean that it's difficult with just the current definitions: With the rounded values of the constants, it just so happens that
The IEEE-754 standard recommends the functions sinPi, cosPi, tanPi, asinPi, acosPi, atanPi, and atan2Pi, which all measure angles in multiples of π. E.g. A more ambitious (experiment-in-a-crate-first) direction might be something like a wrapper type for multiples of π: // PiMul(x) represents the value π * x
struct PiMul<T>(T);
// just for the examples below
type M = PiMul<f32>; With signatures for the arithmetic operators:
And as the main course, trigonometry functions that unwrap/wrap accordingly:
These would then compile to just calls to sinPi and atanPi with no value of π involved: (x * Pi).sin()
PiMul::atan(x) / Pi Admittedly the latter doesn't look quite right. Anyway, @scottmcm's suggestion of having a ZST-type |
This is neat, but all general-purpose programming languages that I know of just have floating-point constants for π, etc., and they seem to get along fine without all this "machinery". In other languages people use Using floating-point numbers in general will invariably lead to some amount of mathematical incorrectness unless you're extremely careful. No, that example code isn't correct, but it's correct down to 1 ULP which is good enough for almost everyone (if those values came from a previous calculation with floats, they will be (potentially) off by at least 1 ULP anyways). |
Well, I wasn't sure either, but I tried with Julia and found out the standard library can do this: julia> π
π = 3.1415926535897...
julia> π == pi
true
julia> typeof(π)
Irrational{:π}
julia> sin(π)
1.2246467991473532e-16
julia> sinpi(1)
0.0
julia> sin(BigFloat(π))
1.096917440979352076742130626395698021050758236508687951179005716992142688513354e-77
julia> sin(BigFloat(π, precision=20000))
4.502850813633212220480382554741730704191676738588386332317097735049223941670455e-6021
julia> lb = Float32(π, RoundDown)
3.1415925f0
julia> ub = Float32(π, RoundUp)
3.1415927f0
julia> lb < π < ub
true
julia> nextfloat(lb) == ub
true Now, dynamic typing does afford a lot of internal complexity without getting in the way of ergonomics, and Rust would have a hard time reaching the same.
Sure, some amount of error is to be expected in many cases, but regardless, the value you have is the value you're using, and you expect the range check to check that value. As in the issue I linked, if the reason for testing the magnitude (supposing the threshold was actually π/2) was that you next pass the angle to |
Proposal
Problem statement
Currently, to use mathematical constants such as π, you need to use
std::f32::consts::PI
.This proposal is for adding associated constants to the
f32
andf64
types so that the constants could be accessed with justf32::PI
for example.Motivation, use-cases
std::f32::consts::PI
is quite long. It takes a while to type out, and makes code difficult to read if written out in full everywhere.Consider
use std::f32::consts as f32c;
but it would be nice for a shorter path to be included in the standard library.f32::PI
and to my disappointment it didn't work and I had to do an internet search to find out the correct path.NAN
,INFINITY
, etc. are associated constants, and it feels inconsistent to me forf32::NAN
to exist, but notf32::PI
.Solution sketches
I have implemented this on my fork: rust-lang/rust@master...pommicket:rust:shorter-paths-for-float-consts
Links and related work
half
andfixed
already have these as associated constants on their types.What happens now?
This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.
The text was updated successfully, but these errors were encountered: