Как мне узнать, подписана ли целочисленная переменная C? - PullRequest
8 голосов
/ 11 сентября 2009

В качестве упражнения я хотел бы написать макрос, который сообщает мне, является ли целочисленная переменная подписанной. Это то, что у меня есть, и я получаю ожидаемые результаты, если попробую это с переменной char с помощью gcc -fsigned-char или -funsigned-char.

#define ISVARSIGNED(V) (V = -1, (V < 0) ? 1 : 0) 

Это портативный? Есть ли способ сделать это, не разрушая значение переменной?

Ответы [ 7 ]

5 голосов
/ 11 сентября 2009
#define ISVARSIGNED(V)  ((V)<0 || (-V)<0 || (V-1)<0)

не меняет значение V. Третий тест обрабатывает случай, когда V == 0.

В моем компиляторе (gcc / cygwin) это работает для int и long, но не для char или short.

#define ISVARSIGNED(V) ((V)-1<0 || -(V)-1<0)

также выполняет работу в двух тестах.

5 голосов
/ 11 сентября 2009

Если вы используете GCC, вы можете использовать ключевое слово typeof, чтобы не перезаписывать значение:

#define ISVARSIGNED(V) ({ typeof (V) _V = -1; _V < 0 ? 1 : 0 })

Это создает временную переменную _V того же типа, что и V.

Что касается переносимости, я не знаю. Он будет работать на машине комплимента для двоих (т.е. всего, что ваш код когда-либо будет выполнять по всей вероятности), и я верю, что он будет работать и на машинах комплимента и знака и величины. В качестве дополнительного примечания, если вы используете typeof, вы можете привести -1 к typeof (V), чтобы сделать его более безопасным (то есть с меньшей вероятностью вызвать предупреждения).

5 голосов
/ 11 сентября 2009
#define ISVARSIGNED(V) ((-(V) < 0) != ((V) < 0))

Без уничтожения значения переменной. Но не работает для 0 значений.

А как же:

#define ISVARSIGNED(V) (((V)-(V)-1) < 0)
1 голос
/ 16 сентября 2009

Другой подход ко всем ответам "сделай его отрицательным":

#define ISVARSIGNED(V) (~(V^V)<0)

Таким образом, нет необходимости иметь специальные случаи для разных значений V, так как ∀ V ∈ ℤ, V ^ V = 0.

1 голос
/ 12 сентября 2009

Это простое решение не имеет побочных эффектов, в том числе преимущество только одного обращения к v (что важно в макросе). Мы используем расширение gcc "typeof", чтобы получить тип v, а затем приводим -1 к этому типу:

#define IS_SIGNED_TYPE(v)   ((typeof(v))-1 <= 0)

Это <=, а не просто <, чтобы избежать предупреждений компилятора в некоторых случаях (если включено). </p>

0 голосов
/ 16 сентября 2009

С какой стати тебе нужен макрос? Шаблоны отлично подходят для этого:

template <typename T>
bool is_signed(T) {
    static_assert(std::numeric_limits<T>::is_specialized, "Specialize std::numeric_limits<T>");
    return std::numeric_limits<T>::is_signed;
}

Который будет работать "из коробки" для всех основных целочисленных типов. Он также потерпит неудачу во время компиляции для указателей, что, вероятно, не произойдет в версии, использующей только вычитание и сравнение.

РЕДАКТИРОВАТЬ : К сожалению, вопрос требует C. Тем не менее, шаблоны являются хорошим способом: P

0 голосов
/ 11 сентября 2009

Отличительной чертой математики со знаком / без знака является то, что при сдвиге номера со знаком вправо копируется самый старший бит. Когда вы сдвигаете число без знака, новые биты равны 0.

#define HIGH_BIT(n) ((n) & (1 << sizeof(n) * CHAR_BITS - 1))
#define IS_SIGNED(n) (HIGH_BIT(n) ? HIGH_BIT(n >> 1) != 0 : HIGH_BIT(~n >> 1) != 0

Таким образом, этот макрос использует условное выражение, чтобы определить, установлен ли старший бит числа. Если это не так, макрос устанавливает его путем побитового отрицания числа. Мы не можем сделать арифметическое отрицание, потому что -0 == 0. Затем мы сдвигаемся вправо на 1 бит и проверяем, произошло ли расширение знака.

Это предполагает арифметику дополнения 2, но обычно это безопасное предположение.

...