C99: может ли мнимая часть комплекса быть отрицательным нулем - PullRequest
7 голосов
/ 04 мая 2011

Можно ли хранить отрицательный ноль в мнимой части комплексного числа с плавающей запятой 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%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

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

Iне могу найти что-либо уместное ни в C99 (n1256.pdf), ни в http://www.knosof.co.uk/cbook/

Ответы [ 5 ]

3 голосов
/ 05 мая 2011

Работает ли _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.

3 голосов
/ 04 мая 2011

Если реализация соответствует Приложению 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 равно положительно ноль при округлении по умолчанию IEEE-754, знаковый бит теряется.

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

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

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

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

1 голос
/ 04 мая 2011

Использование

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

на Target: x86_64-unknown-linux-gnu

Как с, так и без -std=c99 отпечатков

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

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

РЕДАКТИРОВАТЬ Оставшаяся загадка в том, куда идет знак действительной части c?

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

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

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

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

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

См. Также PR 24581

1 голос
/ 04 мая 2011

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

Приложение

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

Отозвано

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

0 голосов
/ 05 мая 2011

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

J.1 Неуточненное поведение

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

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

...