The future of imaginary types

Jens Gustedt, INRIA and ICube, France

2023-12-15

document history

document number date comment
n3206 202312 this paper, original proposal

license

CC BY, see https://creativecommons.org/licenses/by/4.0

1 Discussion

1.1 Imaginary types

The current state if imaginary types in clause 6 and Annex G is not really operational, so we should either make it so or remove them from there.

They are not operational because it is not possible to map their mathematical properties to our type/operator/conversion model as-is. Our model imposes that the result of binary operator in general is one of

This model does not fit for imaginary types because we need result types according to the following table:

operator kind operand types result type
additive
real + imaginary complex
multiplicative
real * imaginary imaginary
imaginary * imaginary real

Our Annex G does exactly propose such a model for the arithmetic itself, but without showing how these would work together with usual arithmetic conversion; in our process of determination of operand conversions and operator result type usual arithmetic conversion is applied whenever the types to not exactly fit. For example with only Annex G, and an implementation that has imaginary types the innocent looking expression

0.5 * I

(where I is the standard macro) has undefined behavior by omission, because the first operand has type double and the second has _Imaginary float and no rule is defined for them as usual arithmetic conversion. On the other hand, the same lexical expression has well-defined behavior on implementations with only complex types but without imaginary types.

Nevertheless, the definition and designated use case of macros like I in case there is an imaginary type build exactly these kind of mixed operations to define imaginary parts in complex expressions of different floating point rank. The use of this kind of expression is well established in the field.

In our current model this kind of operation is only possible if such operations would first map the operands to a complex type and then would return that same type. This would undercut the purpose that the imaginary types could have

There do not seem to be a lot of implementations that provide this feature, at least a first web search that I did with the corresponding keywords only turned up references to the C standard, and to mentions that the corresponding compiler does not implement the feature. But this may be a hen an egg problem; implementations that maybe started looking into this found that the specification is missing, people in the standardization process didn’t bother with it because there are no implementations.

Even worse, we found an implementation (gcc v .13 with glibc) that claims to adhere to Annex G by definining __STDC_IEC_60559_COMPLEX__ but that does not implement imaginary types at all. There are bug reports pointing fingers but no satisfying solution to any of this. For gcc this is really a pity, because they have very good support for complex numbers throughout and even extend this concept to integer types.

Generally, implementions that would start to provide imaginary types from one version to another would probable put their user base into trouble. Not only the kind of expression as discussed has UB, but also types and sizes of expression would change and type inspection via _Generic or typeof would lead to unexpected results. In principle, user code that uses complex types could always test for imaginary or so, but I don’t think anybody ever does that. And there is a good reason for users not to do such tests: there is no platform on which they even could compile fallback code that uses imaginary.

All of this means that imaginary types are not yet of much importance in C’s ecosystem. So a consequence of the lack of a clear specification and the lack of implementations could be to remove the feature from clause 6, Annex G, and all references to these types. This would not be difficult (there are only a few places outside the annex) and would not even be much of a normative change for the core language: the correponding keywords in clause 6 are in the space of reserved identifier, anyhow.

To summarize:

1.2 Imaginary or complex literals

Many implementations that have complex types now have moved to a representation of imaginary literals in the grammar: a floating point literal followed by i (and maybe I) is considered to have a complex value with only an imaginary part. I think that we should add this feature to the C standard, such that we could have support for complex types and operations, even without refering to the correponding header file.

But then for these new literals the same questions as for _Complex_I arise:

I think that for portability it would be good to fix that answer such that there is only one possible outcome.

1.3 Questions

1.3.1 Do we need imaginary types?

Are WG14 members aware of any context where these types would be missing?

1.3.2 Shall we specify usual arithmetic conversion for imaginary types?

That would then probably be delegated to the FP study group.

1.3.3 Should imaginary types (thus Annex G) become mandatory for implementations that have complex types?

1.3.4 Should we remove imaginary types from Annex G?

1.3.5 Do we want to standardize the 1.i notation for imaginary literals?

1.3.6 What type shall 1.i literals have?

1.3.7 Shall we reduce the meaning of __STDC_IEC_60559_COMPLEX__ to just mean “supports complex types according to Annex G”?

This would make gcc conforming. Other compilers that don’t have this macro for exactly that reason (clang for example) could then follow.

1.3.8 Shall we add a feature macro __STDC_IEC_60559_IMAGINARY__?