Как C относится к числу 0 в архитектуре дополнения?

Недавно я изучал систему представления чисел с одним дополнением, и, насколько я понимаю, есть два варианта числа 0. Существует отрицательный ноль (-0) и положительный ноль (+0).

Мой вопрос, на архитектуре дополнения, как именно эта аномалия лечится в C? Делает ли C различие между -0 и +0 или обе эти формы просто рассматриваются как ноль.

Если это так, что и +0, и -0 возвращают TRUE при проверке на ноль, мне интересно, как будет работать следующий пример кода, который вычисляет количество установленных битов в целом числе, если мы введем -0 в качестве его ввода.

int bitcount(int x)
{
    int b;
    for (b = 0; x != 0; b++)
        x &= (x-1);
    return b;
}

Поскольку -0 в своем дополнении имеет все свои биты, установленные на 1, -0 должен возвращать наибольшее количество битов, установленных из любого другого числа; Тем не менее, похоже, что этот код не сможет выполнить условие проверки цикла x != 0и даже не вошел бы в цикл, давая неверный результат.

Можно ли было бы как-то в C, в своей архитектуре дополнения, сделать условие цикла чувствительным к положительным нулям, как в: x != +0 Кроме того, если бы я вычел 1 из +0, я бы получил -0 или -1. Другими словами, +0 - 1 = -0 в архитектуре дополнения?

В общем, чтобы не идти слишком далеко от курса в этом обсуждении, мне просто интересно, как C относится к особенностям числа 0 в архитектуре дополнения.

2 ответа

Решение

Это определяется реализацией, является ли в архитектуре с одним дополнением значение "со знаковым битом и всеми битами значения 1" "представлением ловушки" или нормальным значением. Если это представление ловушки, любая попытка что- либо с ним сделать или вообще создать, провоцирует неопределенное поведение. Если это нормальное значение, это "отрицательный ноль", и существует явный список операций, которым разрешено его производить:

Если реализация поддерживает отрицательные нули, они должны быть сгенерированы только:

  • операторы &, |, ^, ~, << и >> с операндами, которые производят такое значение;
  • операторы +, -, *, / и%, где один операнд - отрицательный ноль, а результат - ноль;
  • составные операторы присваивания на основе вышеуказанных случаев.

Не определено, действительно ли эти случаи генерируют отрицательный ноль или нормальный ноль, и становится ли отрицательный ноль нормальным нулем при хранении в объекте.

(C11 / N1570, раздел 6.2.6.2, пункт 3)

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

Таким образом, все сводится к тому, что поведение вашего примера кода определяется реализацией, и реализация может не определять его услужливо. Вам нужно будет обратиться к руководствам по компилятору и архитектуре для этой гипотетической машины с комплементом, чтобы выяснить, делает ли она то, что вы хотите.

Тем не менее, весь вопрос спорный, потому что никто не производил ЦП без дополнения по крайней мере за 25 лет. Можно надеяться, что в будущем пересмотр стандарта C перестанет учитывать такую ​​возможность; это упростит многие вещи.

Чтобы ответить на ваш вопрос, есть 2 варианта для рассмотрения:

  • если битовая комбинация со всеми установленными битами является представлением прерываний (что явно разрешено стандартом C), передача такого значения в функцию имеет неопределенное поведение.

  • если этот битовый шаблон разрешен, то это одно представление дополнения отрицательного нуля, которое должно сравниваться равным 0, В этом случае функция, как написано, будет иметь определенное поведение и будет возвращать 0 так как начальный тест цикла является ложным.

Результат был бы другим, если бы функция была написана так:

int bitcount32(int x) {
    // naive implementation assuming 31 value bits
    int count = 0, b;
    for (b = 0; b < 31; b++) {
        if (x & (1 << b))
           count++;
        }
    }
    return count;
}

На этой дополняющей архитектуре, bitcount32(~0) оценил бы 31:

(x & (1 << b)) с x аргумент с определенной битовой комбинацией и b в диапазоне для 1 << b определяется, оценивает 1 << bзначение, отличное от результата на двух архитектурах дополнения и знака / величины.

Обратите внимание, что опубликованная реализация имеет неопределенное поведение для аргумента INT_MIN как x-1 вызывает арифметическое переполнение со знаком. Настоятельно рекомендуется всегда использовать беззнаковые типы для побитовых и сдвиговых операций.

Другие вопросы по тегам