C99: the imaginary part of the complex may be a negative zero

Is it possible to keep a negative zero in the imaginary part of a complex float C99?

How should I statically initialize complex constants with a signed imaginary part?

I have a small example, but I can’t understand why a and c same and why -std=c99 changes the results.

 $ cat zero1.c int main() { float _Complex a;a = 0.0 + (__extension__ 0.0iF); float _Complex b;b = 0.0 + (__extension__ -0.0iF); float _Complex c;c = -0.0 + (__extension__ 0.0iF); float _Complex d;d = -0.0 + (__extension__ -0.0iF); printf("a= 0x%016llx\n", *(long long*)(&a)); printf("b= 0x%016llx\n", *(long long*)(&b)); printf("c= 0x%016llx\n", *(long long*)(&c)); printf("d= 0x%016llx\n", *(long long*)(&d)); } $ gcc-4.5.2 -w -std=c99 zero1.c ; ./a.out a= 0x0000000000000000 b= 0x0000000000000000 c= 0x0000000000000000 d= 0x0000000080000000 $ gcc-4.5.2 -w zero1.c ; ./a.out a= 0x0000000000000000 b= 0x8000000000000000 c= 0x0000000000000000 d= 0x8000000080000000 

Quotes from the C99-TC3 and gcc manuals are welcome.

I cannot find anything relevant in C99 (n1256.pdf), but not in http://www.knosof.co.uk/cbook/

+7
source share
5 answers

If the implementation is in accordance with Appendix G and implements the _Imaginary types, then the expression

 b = 0.0 + (__extension__ -0.0iF) 

evaluated as (double)0.0 + (double _Imaginary)(-0.0i) in accordance with the rules in G.5.2 and gives 0.0 - 0.0i .

If the implementation does not provide the _Imaginary type (which is permitted) or otherwise does not comply with Appendix G (also permitted), then this expression is usually evaluated as:

  (double _Complex)(0.0 + 0.0i) + (double _complex)(0.0 - 0.0i) = (double _Complex)((0.0 + 0.0) + (0.0 - 0.0)i) 

Since 0.0 - 0.0 is a positive zero in standard IEEE-754 rounding, the sign is lost.

The moral of the story: if you care about the sign of zero, do not use arithmetic in complex initializers. Since you are using GCC, you can do this instead:

 __real__ c = 0.0f; __imag__ c = -0.0f; 

In my experience, this works at least on gcc-4.0 or so (maybe further).

As for why the behavior was caused by -std=c99 , my best guess is this: the GCC version that you use implements the _Imaginary type, which does not fully match C99; when you specify -std=c99 , _Imaginary support is disabled, and you return to the _Complex matching implementation, which works as described above. However, this is only an assumption; if you are really interested, I would advise you to file a mistake and see what the people accompany you. In fact, I would advise you to file an error anyway. Always log an error .

+3
source

Does _Imaginary_I * -0.0 better than (__extension__ -0.0iF) ?

The upcoming C1x standard will include CMPLX macros, which "act as if the implementation supported imaginary types and definitions:
#define CMPLX(x, y) ((double complex)((double)(x) + _Imaginary_I * (double)(y))) ".

See N1570 , Β§7.3.9.3.

+3
source

It is associated with IEEE floating point behavior as defined by the ISO C standard, which is more stringent with respect to negative zeros. Compiling in a more native form allows the compiler to optimize and thus ignore more stringent rules about such things.

Adding

I do not remember the details, but this is discussed in detail in Appendix F of ISO C99. PDF is available at: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf .

folding

Sorry, I was not mistaken. The ISO C standard does not seem to say anything about negative zeros. This is likely due to how stringent the IEEE FP operations are.

+1
source

Using

gcc version 4.7.0 20110504 (experimental) (GCC)

on Target: x86_64-unknown-linux-gnu

With and without -std=c99 prints

  a = 0x0000000000000000
 b = 0x8000000000000000
 c = 0x0000000000000000
 d = 0x8000000080000000

Therefore, I suspect this is a bug in 4.5.2, which has since been fixed. Perhaps a search in GCC bugzilla and / or mailing lists will make a difference?

EDIT The remaining mystery is where is the sign of the real part c?

EDIT2 The sign of the real part of c is lost because the initializer contains an addition, therefore the expression evaluates to the type float _Complex, therefore

  -0.0 + (__extension__ 0.0iF) = (-0.0, 0.0) + (0.0, 0.0) = (0.0, 0.0)

as -0.0 + 0.0 is 0.0 unless the rounding mode is rounded to negative infinity.

Therefore, to generate a literal (-0, 0) you need something like

  float _Complex c2 = - (0.0 - (__extension__ 0.0iF));

See also PR 24581

+1
source

From Appendix J (Portability Issues):

J.1 Undefined behavior

  • The following are unspecified:
    [...]
    - [...] a negative zero becomes a normal zero when stored in the object (6.2.6.2).

This will do what you want, just more complex.

0
source

All Articles