Допустимо ли использовать memset (, 0,) для массива значений типа double? - PullRequest
37 голосов
/ 07 января 2011

Допустимо ли обнулять массив значений типа double (используя memset (, 0,)) или структуру, содержащую значения типа double?

Вопрос подразумевает две разные вещи:

(1) С точки зрения стандарта C этот UB не является? (на фиксированной платформе, как этот UB ... это просто зависит от плавающего представления, вот и все ...)

(2) С практической точки зрения: нормально ли это на платформе Intel? (независимо от того, что говорит стандарт).

Ответы [ 7 ]

30 голосов
/ 07 января 2011

Приложение F к стандарту C99 гласит:

В этом приложении указана поддержка языка C для стандарта IEC 60559 с плавающей запятой.Стандарт МЭК 60559 представляет собой двоичную арифметику с плавающей точкой для микропроцессорных систем, второе издание (МЭК 60559: 1989) , ранее обозначенное МЭК 559: 1989 и как Стандарт IEEE дляДвоичная арифметика с плавающей точкой (ANSI / IEEE 754-1985) . Стандарт IEEE для радикально-независимой арифметики с плавающей запятой (ANSI / IEEE 854-1987) обобщает двоичный стандарт для удаления зависимостей от оснований и длины слова.МЭК 60559 обычно относится к стандарту с плавающей запятой, как в работе МЭК 60559, формате МЭК 60559 и т. Д. Реализация, которая определяет __STDC_IEC_559__, должна соответствовать спецификациям в этом приложении.Если указана связь между языком C и МЭК 60559, поведение, указанное в МЭК 60559, принимается в качестве ссылки, если не указано иное.

И сразу после

Плавающие типы C соответствуют форматам МЭК 60559 следующим образом:

  • Тип float соответствует одиночному формату МЭК 60559.
  • Тип double соответствует двойному МЭК 60559format.

Итак, если IEC 60559 в основном IEEE 754-1985 и это указывает, что 8 нулевых байтов означают 0,0 (как сказал Дэвид Хеффернан), это означает, что если вы найдете __STDC_IEC_559__ определено, что вы можете безопасно выполнить инициализацию 0.0 с помощью memset.

14 голосов
/ 07 января 2011

Если вы говорите об IEEE754, то стандарт определяет +0,0 для удвоения точности как 8 нулевых байтов.Если вы знаете, что вы поддерживаете плавающую точку IEEE754, то это хорошо определено.

Что касается Intel, я не могу представить компилятор, который не использует IEEE754 на Intel x86 / x64.

5 голосов
/ 08 января 2011

Дэвид Хеффернан дал хороший ответ на часть (2) вашего вопроса. Для части (1):

Стандарт C99 не дает никаких гарантий относительно представления значений с плавающей запятой в общем случае. §6.2.6.1 говорит:

Представления всех типов не определены, кроме случаев, указанных в этом подпункте.

... и этот подпункт больше не упоминает о плавающей точке.

Вы сказали:

(на фиксированной платформе, как этот UB ... это просто зависит от плавающего представления, вот и все ...)

Действительно - существует разница между « undefined поведение», « неопределенное поведение» и « определяемое реализацией поведение»:

  • « undefined поведение» означает, что может произойти все что угодно (включая сбой во время выполнения);
  • « неопределенное поведение» означает, что компилятор может свободно реализовывать что-либо разумное любым способом, каким ему нравится, но нет необходимости документировать выбор реализации;
  • « определенное реализацией поведение» означает, что компилятор может свободно реализовывать что-то разумное любым удобным для него способом и должно задокументировать этот выбор (например, см. здесь для вариантов реализации, задокументированных самой последней версией GCC );

и так, поскольку представление с плавающей точкой является неопределенным поведением, оно может отличаться недокументированным способом от платформы к платформе (где «платформа» здесь означает «комбинация аппаратного обеспечения и компилятора», а не просто »). аппаратное обеспечение ").

(Я не уверен, насколько полезна гарантия того, что double представляется таким, что ноль всех битов равен +0.0, если определено __STDC_IEC_559__, как описано в ответе Matteo Italia, на самом деле на практике. Например, GCC никогда не определяет это , хотя на многих аппаратных платформах используется IEEE 754 / IEC 60559.)

4 голосов
/ 08 января 2011

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

double A[133] = { 0 };

всегда должно работать.Если вам понадобится снова обнулить такой массив позже, и ваш компилятор будет совместим с современным C (C99), вы можете сделать это с помощью составного литерала

memcpy(A, (double const[133]){ 0 }, 133*sizeof(double));

на любом современном компиляторе, это должно бытьэффективен как memset, но имеет то преимущество, что не полагается на конкретную кодировку double.

2 голосов
/ 07 января 2011

Как говорит Matteo Italia, это законно в соответствии со стандартом, но я бы не стал его использовать. Что-то вроде

double *p = V, *last = V + N; // N - count
while(p != last) *(p++) = 0;

как минимум вдвое быстрее.

0 голосов
/ 09 января 2011

Допустимо использовать memset. Вопрос в том, генерирует ли он битовую комбинацию, где массив [x] == 0.0 имеет значение true. Хотя базовый стандарт C не требует, чтобы это было правдой, мне было бы интересно услышать примеры, где это не так!

Похоже, что memset эквивалентно 0.0 в IBM-AIX, HP-UX (PARISC), HP-UX (IA-64), Linux (IA-64, я думаю).

    {
    double dFloat1 = 0.0;
    double dFloat2 = 111111.1111111;

    memset(&dFloat2, 0, sizeof(dFloat2));

    if(dFloat1 == dFloat2)
    {
        fprintf(stdout, "memset appears to be equivalent to = 0.0\n");
    }
    else
    {
        fprintf(stdout, "memset is NOT equivalent to = 0.0\n");
    }
}
0 голосов
/ 07 января 2011

Ну, я думаю, что обнуление является «законным» (в конце концов, это обнуление обычного буфера), но я не знаю, позволяет ли стандарт предположить что-либо о результирующем логическом значении.Я полагаю, что стандарт C оставляет его неопределенным.

...