Is it possible to store negative zero in imaginary part of C99 complex float?
How I should statically initialize complex constants with signed imaginary part?
I have a small example, but I can't understand, why a
and c
are same and why -std=c99
changes 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
Quotations from C99-TC3 and gcc manuals are welcome.
I cant find anything relevant in C99 (n1256.pdf) nor in http://www.knosof.co.uk/cbook/
If an implementation conforms to Annex G and implements the _Imaginary
types, then the expression
b = 0.0 + (__extension__ -0.0iF)
is evaluated as (double)0.0 + (double _Imaginary)(-0.0i)
according to the rules in G.5.2, and yields 0.0 - 0.0i
.
If the implementation does not provide an _Imaginary
type (which is allowed), or otherwise does not conform to Annex G (also allowed), then this expression is typically 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)
Because 0.0 - 0.0
is positive zero in IEEE-754 default rounding, the signbit is lost.
Moral of the story: if you care about the sign of zero, don't use arithmetic in complex initializers. Since you're using GCC, you can do this instead:
__real__ c = 0.0f;
__imag__ c = -0.0f;
In my experience, this works back to at least gcc-4.0 or so (maybe farther).
As to why the behavior was triggered by -std=c99
, my best guess is the following: the version of GCC that you're using implements an _Imaginary
type that is not fully conformant with C99; when you specify -std=c99
, support for _Imaginary
is turned off, and you fall back on a conformant _Complex
implementation that works as I described above. This is only a guess however; if you're really curious, I would encourage you to file a bug and see what the maintainers say. Actually, I would encourage you to file a bug anyway. Always file a bug.
Does _Imaginary_I * -0.0
work better than (__extension__ -0.0iF)
?
The upcoming C1x standard will include the CMPLX
macros, which “act as if the implementation supported imaginary types and the definitions were:#define CMPLX(x, y) ((double complex)((double)(x) + _Imaginary_I * (double)(y)))
.”
See N1570, §7.3.9.3.
It has to do with IEEE floating-point behavior as specified by the ISO C standard, which is more strict about negative zeros. Compiling in a more native form allows the compiler to optimize, and thus disregard stricter rules, about such things.
Addendum
I don't remember the details, but this is discussed in depth in Appendix F of the ISO C99 standard. PDF available at: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf .
Retracted
Sorry, I remembered wrong. The ISO C standard apparently does not dictate anything about negative zeros. It is probably has to do with how strict the IEEE FP operations are.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With