C99: может ли мнимая часть комплекса быть отрицательным нулем

можно ли хранить отрицательный нуль в мнимой части комплексного поплавка C99?

как я должен статически инициализировать комплексные константы со знаковой мнимой частью?

у меня есть маленький пример, но я не могу понять, почему a и c такие же, и почему -std=c99 результаты изменений.

$ 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%016llxn", *(long long*)(&a));
    printf("b= 0x%016llxn", *(long long*)(&b));
    printf("c= 0x%016llxn", *(long long*)(&c));
    printf("d= 0x%016llxn", *(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

цитаты из руководств C99-TC3 и gcc приветствуются.

Я не могу найти ничего подходящего в C99 (n1256.pdf) ни в http://www.knosof.co.uk/cbook/

5 ответов


если реализация соответствует приложению G и реализует _Imaginary типы, то выражение

b = 0.0 + (__extension__ -0.0iF)

оценивается как (double)0.0 + (double _Imaginary)(-0.0i) согласно правилам G. 5.2, и дает 0.0 - 0.0i.

если реализация не обеспечивает _Imaginary тип (который разрешен) или иным образом не соответствует приложению G (также разрешено), тогда это выражение обычно оценивается как:

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

, потому что 0.0 - 0.0 is положительное ноль в Стандарт IEEE-754 округления по умолчанию, signbit теряется.

мораль истории: если вы заботитесь о знаке нуля, не используйте арифметику в сложных инициализаторах. Поскольку вы используете GCC, вы можете сделать это вместо этого:

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

по моему опыту, это работает по крайней мере до gcc-4.0 или около того (может быть, дальше).

о том, почему поведение было вызвано -std=c99, мое лучшее предположение следующее: версия GCC, которую вы используете, реализует _Imaginary тип не полностью соответствует C99; при указании -std=c99 поддержка _Imaginary отключается, и ты падаешь обратно на совместимую _Complex реализация, которая работает, как я описал выше. Однако это только предположение; если вам действительно интересно, я бы посоветовал вам подать ошибку и посмотреть, что скажут сопровождающие. На самом деле, я бы посоветовал вам все равно подать ошибку. всегда файл ошибка.


тут _Imaginary_I * -0.0 работать лучше, чем (__extension__ -0.0iF)?

предстоящий стандарт C1x будет включать в себя CMPLX макросы, которые " действуют так, как будто реализация поддерживает мнимые типы и определения:
#define CMPLX(x, y) ((double complex)((double)(x) + _Imaginary_I * (double)(y)))."

посмотреть N1570, §7.3.9.3.


Это связано с поведением IEEE с плавающей запятой, как указано стандартом ISO C, который более строг в отношении отрицательных нулей. Компиляция в более родной форме позволяет компилятору оптимизировать и, следовательно, игнорировать более строгие правила о таких вещах.

дополнительное соглашение

Я не помню деталей, но это подробно обсуждается в приложении F стандарта ISO C99. PDF доступен по адресу: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf .

убирается

Извините, я неправильно запомнила. Стандарт ISO C, по-видимому, ничего не диктует о отрицательных нулях. Вероятно, это связано с тем, насколько строгими являются операции IEEE FP.


используя

gcc версия 4.7.0 20110504 (экспериментальная) (GCC)

on Target: x86_64-unknown-linux-gnu

и -std=c99 печать

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

поэтому я подозреваю, что это ошибка в 4.5.2, которая с тех пор была исправлена. Возможно, поиск в GCC bugzilla и / или списках рассылки что-то обнаружит?

редактировать оставшаяся тайна заключается в том, куда идет знак реальной части c?

EDIT2 знак из реальной части c теряется, потому что инициализатор содержит дополнение, поэтому выражение оценивается как тип float _Complex, следовательно

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

as -0.0 + 0.0 равно 0.0, если режим округления не является круглым в направлении отрицательной бесконечности.

следовательно, для генерации литерала (-0, 0) вам нужно что-то вроде

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

см. также PR 24581


Из Приложения J (Вопросы Переносимости):

J. 1 неопределенное поведение

  1. следующие не указаны:
    [...]
    - Становится ли [ ... ] отрицательный ноль нормальным нулем при хранении в объекте (6.2.6.2).

Это сделает то, что вы хотите, просто более сложным.