41

According to the C++ ISO spec, §26.2/2:

The effect of instantiating the template complex for any type other than float, double or long double is unspecified.

Why would the standard authors explicitly add this restriction? This makes it unspecified, for example, what happens if you make complex<int> or a complex<MyCustomFixedPointType> and seems like an artificial restriction.

Is there a reason for this limitation? Is there a workaround if you want to instantiate complex with your own custom type?

I'm primarily asking this question because of this earlier question, in which the OP was confused as to why abs was giving bizarre outputs for complex<int>. That said, this still doesn't quite make sense given that we also might want to make complex numbers out of fixed-points types, higher-precision real numbers, etc.

Community
  • 1
  • 1
templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 5
    I had to laugh after seeing your previous answer, but it really is a good question. – chris Jun 19 '12 at 20:19
  • 10
    @chris- I felt bad giving that answer without being able to give a high-level reason for it. I'm usually good at saying "this is a bizarre edge case of C++ for reasons X, Y, and Z," but this time I have no idea what's up. – templatetypedef Jun 19 '12 at 20:20
  • 4
    "Standardizing complex has been discussed on the committee off and on for over a decade and there exists significant resistance from at least one vendor for supporting it." http://lists.cs.uiuc.edu/pipermail/cfe-dev/2012-March/020398.html –  Jun 19 '12 at 20:26
  • I can't speak for the designers, but as a practical matter lots of the math you need to implement uses methods that really make sense only for floating point types - square roots, atan2s, etc. – tmpearce Jun 19 '12 at 20:27
  • Just take a look at the implementation in the `complex` header file and see how much transcendentals are used inside the template. Are they all available for integers or fixed-point numbers, because computing even such a simple thing as the argument requires `atan2()`. – Hristo Iliev Jun 19 '12 at 20:41
  • 1
    @HristoIliev- The fact that it's *difficult* to implement `complex` for arbitrary types doesn't necessarily mean that it should be *impossible* to do so. The C++ spec could have defined a new set of traits (similar to container requirements or allocator requirements) saying "if you can implement the following, then you can be stored in a `complex`." – templatetypedef Jun 19 '12 at 20:44
  • 5
    "Unspecifed" is not the same as "Undefined" or "ill formed." Unspecified, in standardese, means "This is potentially legal, but the Standard doesn't require it, and you need to check your compiler docs to see if it's allowed in your actual implementation." – John Dibling Jun 19 '12 at 20:47
  • 1
    To be fair, leaving behavior unspecified is exactly the opposite of a limitation, as far as compiler developers are concerned. – Jon Purdy Jun 20 '12 at 00:53
  • 1
    Yes, the committee just weasels out of having to define what, precisely, a `T` needs to support for `complex` to be allowed. If you overload the transcendentals and `abs()` (finally allowed in C++11 (C++98 allowed full specialisations, but no overloads of names in the `std` namespace)) for your infinite-precision or fixed-point arithmetic type, it will just work. I would consider something like `__isinf` being used in the implenentation w/o a way to specialise for your type a quality-of-implementation bug. Even then, you could still specialize the affected methods for your type explicitly. – Marc Mutz - mmutz Jun 24 '12 at 15:41

2 Answers2

32

You can't properly implement many of the std::complex operations on integers. E.g.,

template <class T>
T abs(const complex<T> &z);

for a complex<long> cannot have T = long return value when complex numbers are represented as (real,imag) pairs, since it returns the value of sqrt(pow(z.real(), 2) + pow(z.imag(), 2)). Only a few of the operations would make sense.

Even worse, the polar named constructor cannot be made reliable without breaking the default constructor and vice versa. The standard would have to specify that "complex integers" are Gaussian integers for them to be of any use, and that one of the constructors is severely broken.

Finally, how would you like your "complex integer division" served, and would you like a "complex remainder" with that? :)

Summarizing, I think it would be more sensible to specify a separate gaussian_int<T> type with just a few operations than graft support for integral T onto std::complex.

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 1
    Funny how we chose the exact same function to illustrate. :) – Luchian Grigore Jun 19 '12 at 20:28
  • Thanks for introducing a solid mathematical reason for the decision. I think this provides an elegant theoretical reason for the decision. I had not previously heard of the Gaussian integers, so thanks for bringing them up! – templatetypedef Jun 23 '12 at 20:07
  • @templatetypedef: Thank you and +1 for the question. I'd never considered what would happen when `complex` was templated on integral types. [I'm not a mathematician, though, and if anyone can prove me wrong on this, please do.] – Fred Foo Jun 23 '12 at 22:45
  • 1
    I know it was only meant rhetorically, but the Gaussian integers are a Euclidean domain (and even norm-Euclidean), and so division with remainder behaves essentially the same way as it it does with the ordinary integers; you just have a few more choices of rounding modes. –  Apr 18 '15 at 17:41
12

Probably for compatibility with the helper functions. For example:

template<class T> T abs (const complex<T>& x);

If T == int, abs would return int, which would mean a massive loss in precision.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • We already accept massive loss of precision in integer division. `5/3 = 1`. I think we could similarly accept `abs(std::complex(5, 3))` returning either `5` or `6`. It's maybe a niche, but I've spent most of my career working with complex integers. For people like me, it's a great shame they are not supported. – Harry Oct 15 '21 at 20:28