-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Complex numbers #3892
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
base: master
Are you sure you want to change the base?
Complex numbers #3892
Conversation
|
Labeling this T-lang because the desire to make this FFI-compatible is a lang matter. |
|
It's worth pointing out another big issue with this is that the canonical In particular, while I'm not super compelled by the argument that C supports this, therefore the standard library needs to support this. I think that guaranteeing a |
I think that polar form almost always is the more optimal form, at least in my experience. But the ABIs do use rectangular, e.g. from x86:
so it makes sense that an interchange type matches that, and users can translate to/from a polar repr at the FFI boundary if needed. But this reasoning is definitely something to have in the RFC's rationale & alternatives. |
|
Right: I guess my main clarification here was that due to the polar-orthogonal discrepancy, it shouldn't be a canonical Rust type (e.g. |
|
|
||
| The definition of complex numbers in the C99 standard defines the _memory layout_ of a complex number but not its _calling convention_. | ||
| This makes crates like `num-complex` untenable for calling C FFI functions containing complex numbers without at least a level of indirection (`*const Complex`) or the like. | ||
| Only in `std` is it possible to make an additional repr to match the calling convention that C uses across FFI boundaries. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is FFI compatibility limited to std only? It's just like how we have all the types in crate@libc. What necessitates complex becominga type in something like crate@libc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's like va_list (core::ffi::va_list::VaListImpl) can only be implemented in libcore.
|
Thanks everyone for the feedback! I have incorporated as much as I can into the RFC. |
| impl Complex<f64> { | ||
| fn angle(self) { | ||
| f32::atan2(self.re(), self.im()) | ||
| } | ||
| fn from_polar(modulus: f32, angle: f32) -> Complex<f32> { | ||
| Complex::new(modulus * f32::cos(angle), modulus * f32::sin(angle)) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably want to return something here? And not use the f32 types for f64
That being said: I think polar conversions should be put into "future possibilities" since they aren't needed for basic support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was trying to write something to help with @clarfonthey's polar proposal, but if they are fine with this becoming a future possibility, then OK!
Co-authored-by: kennytm <kennytm@gmail.com>
Co-authored-by: Juniper Tyree <50025784+juntyr@users.noreply.github.com>
Co-authored-by: Juniper Tyree <50025784+juntyr@users.noreply.github.com>
|
@clarfonthey one of the other reasons I don't agree with polar representation is that it makes it very hard to define complex integers (Gaussian integers) which I included as a future possibility and which are useful for dealing with discrete geometry. |
But What the stdlib could do is to make this representation private, and reserve the right to change it without it being considered a breaking change. But why would this change ever happen? If changing the repr of this type realistically won't happen, it's useful to make this repr a public guarantee (not only it aids FFI, it also aids unsafe code, such as people writing inline asm to manipulate such a type without the need to pass through a conversion step) |
|
The conversion between a polar and orthogonal form isn't lossless, so, it effectively can't be done "automatically" or "as an implementation detail." You need trigonometric functions to do it, and while the conversion is algebraically closed, it's certainly more complicated to do exactly and most people prefer to just use floats instead. My point here isn't that we need to decide; the issue is that the decision itself means that we shouldn't decide, and instead avoid having a standard This doesn't preclude adding |
|
@clarfonthey IMO if you consider the fact that complex integers are only
really supported orthogonally, then it becomes clear that there is only
really one choice. Though polar representations are more accurate, they
simply wouldn't work with complex integers (Gaussian integers) which I
proposed as a future possibility. Rust has to decide in favor of the
orthogonal representation if we choose to support Gaussian integers.
…On Fri, 5 Dec, 2025, 00:37 Clar Fon, ***@***.***> wrote:
*clarfonthey* left a comment (rust-lang/rfcs#3892)
<#3892 (comment)>
The conversion between a polar and orthogonal form isn't lossless, so, it
effectively can't be done "automatically" or "as an implementation detail."
You need trigonometric functions to do it, and while the conversion is
algebraically closed, it's certainly more complicated to do exactly and
most people prefer to just use floats instead.
My point here isn't that we need to decide; the issue is that the decision
itself means that we *shouldn't* decide, and instead avoid having a
standard Complex type for the standard library.
This doesn't preclude adding std::ffi::Complex which allows
ABI-compatibility with C's _Complex, however, I don't think that such a
type should be made standard for the language *because* of the fact that
there are so many different ways to go about it.
—
Reply to this email directly, view it on GitHub
<#3892 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/BLL7PYEKKLORP5MEG4D3QK34ABPK3AVCNFSM6AAAAACNY66HSWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTMMJTGEZTINZVGU>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
|
Rust doesn't have to support them, though. Gaussian integers, however useful, are not a primitive that the language needs to offer to everyone and maintain. |
| ## Guide-level explanation | ||
| [guide-level-explanation]: #guide-level-explanation | ||
|
|
||
| `Complex<T>` numbers can be instantiated as of any type using `Complex::new(re, im)` where `re` and `im` are of the same type (this includes all numbers). | ||
| ```rust | ||
| let x = Complex::new(3.0, 4.0); | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussion: where should this type live?
(redirecting discussion from the main PR thread to keep things organized)
text/3892-complex-numbers.md
Outdated
| ## Rationale and alternatives | ||
| [rationale-and-alternatives]: #rationale-and-alternatives | ||
|
|
||
| The rationale for this type is mostly FFI: C libraries that may be linked from Rust code currently cannot provide functions with direct struct implementations of Complex - they must be hidden under at least a layer of indirection. However, it is not always possible to write a C complex-valued function that wraps the first function in a pointer. Thus, FFI becomes a problem if such complex-valued functions are passed by value and not by reference. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The C spec says that T _Complex gets treated as T[2] in memory. It is my understanding that most ABIs effectively say the same. Could you give some concrete examples of platforms where this isn't the case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on powerpc64-linux-gnu, returning double _Complex doesn't do the same thing as returning a struct with a field of type double[2]: https://gcc.godbolt.org/z/hh7zYcnK6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add into the RFC
As binary floating point cannot represent So I don't see how this is a valid discrepancy in the first place, no sane library will only provide a If we are going to have a
Providing only So IMO we either:
Note that Gaussian integers are not the only type of complex integers. |
Deciding the in-memory representation is the easy part, the hard part is how exactly complex numbers are passed by value in function arguments and return values -- I recall reading ABI specs that treat complex numbers specially such that they don't really match the ABI of any other single type (so it has to be handled specially by rustc and can't just be an existing type), though I can't currently recall which ABI specs. |
|
@programmerjake you're right - the calling convention of complex numbers is really not defined very well, which is why in my opinion No. 2 seems untenable. Sometimes complex numbers are passed as a struct, sometimes SSE registers - it really is untenable for anything other than STD to match the calling convention. (I am not an AI - I use dashes to denote pauses very frequently.) |
Added theoretical C code example and alternatives for complex number handling in FFI.
Clarified rationale for Complex type regarding FFI and C calling conventions.
text/3892-complex-numbers.md
Outdated
| ## Drawbacks | ||
| [drawbacks]: #drawbacks | ||
|
|
||
| If there is suddenly a standard-library Complex type, people may rush to include it in their current implementations, which would leave people behind if they didn't know about it. I really don't think this is a drawback though, since similar things have happened in Rust before: the inclusion of `OnceCell` in Rust, for example. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really understand what exactly this means. Who would want to rush to include complex numbers in their code? And who would be be left behind?
I think the biggest downside here is the increased complexity and API surface and implementation maintenance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
github put this reply in the wrong thread, afaict it was intended to be here.
|
No, what I was intending to say was that the fact that complex numbers
would be introduced would make some libraries use the std complexes
immediately whereas others would still use num-complex. I'll add
implementation complexity though.
…On Sun, 7 Dec, 2025, 06:54 nora, ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In text/3892-complex-numbers.md
<#3892 (comment)>:
> +impl Div for Complex<f32> { // calls to __divsc3 will be required here for implementation details and corresponding real types will also be implemented
+ type Output = Self;
+
+ fn div(self, other: Self) -> Self::Output;
+}
+impl Div for Complex<f64> { // calls to __divdc3 will be required here for implementation details and corresponding real types will also be implemented
+ type Output = Self;
+
+ fn div(self, other: Self) -> Self::Output;
+}
+```
+The floating point numbers shall have sine and cosine and tangent functions, their inverses, their hyperbolic variants, and their inverses defined as per the C standard and with Infinity and Nan values defined as per the C standard.
+## Drawbacks
+[drawbacks]: #drawbacks
+
+If there is suddenly a standard-library Complex type, people may rush to include it in their current implementations, which would leave people behind if they didn't know about it. I really don't think this is a drawback though, since similar things have happened in Rust before: the inclusion of `OnceCell` in Rust, for example.
I don't really understand what exactly this means. Who would want to rush
to include complex numbers in their code? And who would be be left behind?
I think the biggest downside here is the increased complexity and API
surface and implementation maintenance.
—
Reply to this email directly, view it on GitHub
<#3892 (review)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BLL7PYAVAPV6XHQUYQJ6OO34ANND5AVCNFSM6AAAAACNY66HSWVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMZTKNBYGUYTOOJUGA>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Clarify potential drawbacks of a standard-library Complex type and its impact on implementation.
This RFC proposes FFI-compatible complex numbers to help scientific computing library authors use non-indirected complexes.
I apologise in advance to
num-complexRendered