Подтвердить что ты не робот

Является ли законным использовать memset (, 0,) в массиве удвоений?

Является ли он законным для нулевого массива удвоений (с использованием memset (, 0)) или структуры, содержащей удвоения?

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

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

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

4b9b3361

Ответ 1

В стандарте C99 Приложения F говорится:

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

И, сразу после,

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

  • Тип float соответствует стандарту IEC 60559.
  • Тип double соответствует двойному формату IEC 60559.

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

Ответ 2

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

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

Ответ 3

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

double A[133] = { 0 };

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

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

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

Ответ 4

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

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

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

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

Ты сказал:

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

В самом деле - существует разница между "undefined поведение", "неопределенным поведением" и "поведение, определяемое реализацией":

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

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

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

Ответ 5

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

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

по крайней мере, в два раза быстрее.

Ответ 6

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

Ответ 7

Это "законный" для использования memset. Проблема заключается в том, создает ли бит шаблон, где array [x] == 0.0 является истинным. Хотя базовый стандарт 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");
    }
}