TLDR; Вызывает ли следующий код неопределенное (или неопределенное) поведение?
#include <stdio.h>
#include <string.h>
void printme(void *c, size_t n)
{
/* print n bytes in binary */
}
int main() {
long double value1 = 0;
long double value2 = 0;
memset( (void*) &value1, 0x00, sizeof(long double));
memset( (void*) &value2, 0x00, sizeof(long double));
/* printf("value1: "); */
/* printme(&value1, sizeof(long double)); */
/* printf("value2: "); */
/* printme(&value2, sizeof(long double)); */
value1 = 0.0;
value2 = 1.0;
printf("value1: %Lf\n", value1);
printme(&value1, sizeof(long double));
printf("value2: %Lf\n", value2);
printme(&value2, sizeof(long double));
return 0;
}
На моей машине x86-64 результат зависит от конкретных флагов оптимизации, переданных компилятору (gcc-4.8.0, -O0 и -O1).
С -O0 я получаю
value1: 0.000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
value2: 1.000000
00000000 00000000 00000000 00000000 00000000 00000000 00111111 11111111
10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
В то время как с -O1 я получаю
value1: 0.000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
value2: 1.000000
00000000 00000000 00000000 00000000 00000000 01000000 00111111 11111111
10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
Обратите внимание на лишнюю 1 в предпоследней строке. Кроме того, раскомментирование инструкций печати после memset приводит к исчезновению 1. Кажется, это основано на двух фактах:
- long double дополняется, т. е. sizeof(long double) = 16, но используется только 10 байт.
- вызов memset может быть оптимизирован
- биты заполнения длинных двойников могут измениться без уведомления, т. е. операции с плавающей запятой над значением1 и значением2, по-видимому, скремблируют биты заполнения.
Я компилирую с -std=c99 -Wall -Wextra -Wpedantic
и не получаю предупреждений, поэтому я не уверен, что это случай строгого нарушения алиасинга (но вполне может быть). Прохождение -fno-strict-aliasing
ничего не меняет.
Контекст — это ошибка в библиотеке HDF5, описанная здесь. HDF5 немного повозится, чтобы выяснить собственное битовое представление типов с плавающей запятой, но он запутается, если биты заполнения не остаются нулевыми.
So:
- Это неопределенное поведение?
- Является ли это строгим нарушением алиасинга?
Спасибо.
редактировать: это код для printme. Я признаю, что просто вырезал и вставил откуда-то, не обращая на это особого внимания. Если вина здесь, я буду ходить вокруг стола со спущенными штанами.
void printme(void *c, size_t n)
{
unsigned char *t = c;
if (c == NULL)
return;
while (n > 0) {
int q;
--n;
for(q = 0x80; q; q >>= 1)
printf("%x", !!(t[n] & q));
printf(" ");
}
printf("\n");
}
-O0
и-O1
. - person lurker   schedule 07.09.2013long double
, и я благодарен, что GCC делает ее доступной, но я действительно ненавижу выбор GCC для представления ее 16 байтами. - person Pascal Cuoq   schedule 07.09.2013-m96bit-long-double
. См. gcc.gnu.org/onlinedocs/gcc/i386-and -x86_002d64-Options.html - person andreabedini   schedule 07.09.2013long double
привела к серьезному снижению производительности современных чипов. - person R.. GitHub STOP HELPING ICE   schedule 07.09.2013