
I appreciate everyone's comments (and I'll respond to a number below), but it seems interest in this is not widespread enough to justify trying to formally include it. I'll probably clean it up a bit and post it on my own web-site at some point in case someone somewhere might find it useful. It was a good exercise in humility to note that, judging from the message frequency, people are at least a hundred times more interested in the new logo for the Boost web-site. :) ------------------------------------------------------------ Regarding specific comments: Roland Schwarz wrote:
For some applications, polar complex numbers have significant advantages over Cartesian complex (e.g., std::complex) in efficiency and numerical accuracy.
Just out of curiosity: Can you give me an example of an algorithm where this would be of advantage?
Well, Hubert Holin gave one: tracking orbits (trajectories in polar coordinates). There are actually a number of subtleties to be aware of in mapping a physical 2D plane to the complex plane, so in that case, I would actually probably use a true polar_vector2D class rather than polar_complex. However, for field equations (including those from fluid dynamics, my area), using tools from complex analysis can create new, simpler formulations of problems in the complex plane. If the resulting mapping has a high order of circular symmetry, then polar_complex numbers have advantages. The most obvious use of polar_complex numbers would be in wave mechanics. The phase and magnitude are often weakly coupled; you can sometimes work with just one component in different parts of the calculations. At the lowest level, polar_complex numbers are best when the primary operations are multiplications/divisions and powers/roots. When you start adding/subtracting, then cartesian representations begin to win out (because of the cost of the trig functions). There's another advantage of polar_complex numbers that I didn't spell out separately: *development* efficiency, i.e. how quickly one can write an acceptable program for a simulation prototype or a quick-and-dirty calculation. Many problems (in fact, a majority of those I have worked with) are more naturally formulated in polar form; it's therefore easier to write a program that uses that representation. This is just the old programming adage "Think/write in the problem domain, not the language domain.". I'd address run-time efficiency later in those cases where it was a concern; I haven't yet seen any cases where the polar_complex representation was obviously inefficient. Hubert Holin wrote:
The main problems I foresee are in the commingling of the two different classes. It is interesting to see a complex number (among other ways) either as its two canonical coordinates, or in a polar representation, but sometimes we just want to switch between the two for a given instance. So I fear a substantial amount of typical run time will be spent doing conversions.
I defined explicit conversions to and from std::complex, but I used them fairly rarely. If the problem truly belongs in the polar representation, then you won't need many conversions. In fact, I wrote polar_complex in the first place in a case when I found myself very frequently calling abs, arg, and polar using std::complex -- the problem didn't belong in the cartesian representation. But you are right that the relation between them needs careful consideration; that was on my list of issues to address if I "boosted" it.
There is another, perhaps deeper, problem to consider, with the representation of complex numbers (and others), as evidenced by a thread on comp.std.c++ a couple of years back: layout guaranties. People wanted, in essence, to be able to break encapsulation when it suited them (for efficiency in time-critical code). The discussion also encompassed thoughts about the polar representation (which, as you remarked, also has advantages). As far as I know, there has been no tangible result to that thread.
In short, I am not sure a library for complex numbers in polar form would be worthwhile, but I certainly think that if you could make the above situation progress (as in: write a formal proposal), it would be a great win for us all.
Well, I needn't worry about polar_complex layout because there are no C or FORTRAN equivalents to be layout compatible with. :) Regarding std::complex, just about a month ago, I posted to comp.lang.c++.moderated about these very issues. The current status is in this paper: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1589.html The layout guarantee seems certain, and it lays out possible solutions for the second issue. I don't see much that I can add other than to give my recommendation on the solution and prod them to pick one already. I've already been doing both on clc++m; I suppose I could write Howard Hinnant directly, but he's responded to several threads so he's probably read most of my posts. This is becoming a digression, but my opinion, for what it's worth: std::complex should never have been encapsulated as it was in the first place. And to justify this, in addition to the efficiency issues, there is clarity of the code's intent. Consider: 1a) z = complex<double>(z.real(), 1); 2a) z.imag() = 1; 1b) z = complex<double>(z.real(), z.imag() + 1); 2b) z.imag() += 1; I think it's clearly much easier to read the intent of the code in the second cases. Matt Austern wrote:
But is it likely that you would want to use a package that provides complex numbers in polar form? My experience is that if you're working with complex numbers that you want to think of in the form r exp(i phi), then you'll probably just do the work of separating out the magnitude and phase ahead of time in your equations, and then compute with them separately. After all: if you're doing something where the magnitude-phase form is most convenient, it usually means that putting numbers in that form makes the equations simpler.
True to a degree, though such separation is not always possible, especially in non-linear situations. And a similar argument is true for cartesian complex: if you're doing something where the real-imaginary form is most convenient, it also usually means that putting numbers in that form makes the equations simpler. But it's usually easier to keep track of, for instance, just vector< complex<double> > Numbers; than vector<double> NumbersReal; vector<double> NumbersImag; and there's usually no harm in combining them like that (unless of course complex is over-encapsulated, but I digress again. . .). Similarly, vector< polar_complex<double> > Wave; is simpler to deal with than vector<double> WaveAmplitude; vector<double> WaveMagnitude; so combine them if there's no harm in doing so. This is appealing to the "development efficiency" (and code clarity) argument I made above.
Still, from the discussion on comp.std.cpp a few years back, I recall that there was a feeling that having multiple representations (say cartesian and polar) at the same time would have been nice. Of course, it would have been a bloated beast (at least in a naive implementation), and a slow one at that (to keep track of the various changes, in a naive implementation still). Furthermore it clashed with the desire to have some layout guaranty which played nice with C (that line of reasoning seems to have flown out in limbo, however).
This illustration is merely there as a reminder that the wish for concurrent multiple points of views of a given abstract entity is not a futile one, if one that I fear can't be translated from mathematics to CS. We've had another illustration of that recently with the affine space/vector space discussion in a GUI thread a few days back here (which also happens to be relevant to this discussion as well, at least in two dimensions).
Well now you are touching on an entirely separate point. For a mathematical primitive used in numerical work, making the same class hold different representations is a terrible idea. Pick the representation you need and stick with it, converting between representations only if/when you must. If you find yourself converting constantly, then you have picked the wrong algorithm or need another representation altogether. And how many representations should you implement in one class? Why cartesian and polar coordinates but not parabolic, elliptical, bipolar, etc.? ------------------------------------------------------------ Again, I appreciate all the comments, but the interest obviously isn't there for developing the class further. Thanks, Mickey Moore