2009-09-11 2 views
7

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

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

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

+0

Это любопытная проблема, но я гораздо более заинтригован тем, что вы планировали использовать для этой информации является. Есть ли шанс поделиться? –

+0

Вот почему C++ имеет RTTI. :) –

+2

@jeffamaphone: На самом деле, здесь шаблоны сияют на C++. – sbi

ответ

4
#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) 

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

+0

Лучшее, что я видел. Насколько я понимаю, портативный, стандартно-совместимый, точный. –

+1

Не различает подписанный/unsigned short и char. Эти типы повышаются до int при оценке выражения < == >. – mob

+0

Если вы хотите, чтобы он работал для 'short' и' char', вы, вероятно, могли бы использовать переменную 'char' перед ее использованием. Это должно справиться с большинством проблем с переполнением. Я думаю ... –

5
#define ISVARSIGNED(V) ((-(V) < 0) != ((V) < 0)) 

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

насчет:

#define ISVARSIGNED(V) (((V)-(V)-1) < 0) 
+3

#define ISVARSIGNED (V) ((-V <0)! = (V <0)) – plinth

+0

да я не должен был иметь C & Pd этот избыточный материал ;-) – ypnos

+0

@plinth, вы забыли «лишние» парсеры вокруг «V», которые делают его безопасным от безумных расширений макросов – rmeador

5

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

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

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

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

+0

В C++ он гарантированно работает стандартом, независимо от целочисленного представления (для n бит значение равно 2^n - 1). У меня нет стандартной версии C (любой из них). –

+0

Меня тоже нет, но я помню, как читал в Википедии (источник всех вещей истинный: P), что стандарт C позволяет три представления, которые я перечислял. Не то, чтобы кто-то их использовал больше ... –

-1

Отличительной чертой подписанной/неподписанной математики является то, что при правильном сдвиге подписанного числа копируется самый старший бит. Когда вы переносите число без знака, новые биты 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, но это обычно безопасное предположение.

+0

У вас есть источник этих предположений о поведении бит-сдвигов? –

+0

В стандарте C99 (раздел 6.5.7) говорится, что сдвиг вправо знака, подписанного, отрицательного, определяется реализацией. Моя интерпретация заключается в том, что на машине дополнения 2 появится расширение знака. Поскольку C не является специфичным для архитектуры дополнений 2, они не выйдут и не скажут об этом. –

+0

У вас будет больше ПК с помощью 'CHAR_BITS' вместо волшебного' 8'. (Да, я знаю, только немногие из нас не работают на машинах, где байт 8 бит. Тем не менее.) – sbi

1

Это простое решение не имеет побочных эффектов, в том числе полезно только ссылаться на v один раз (что важно в макросе). Мы используем ССАГПЗ расширение «TypeOf», чтобы получить тип V, а затем отливали -1 к этому типу:

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

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

0

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

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; 
} 

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

EDIT: К сожалению, этот вопрос требует C. Тем не менее, шаблоны являются хорошим способом: P

0

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

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

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

Смежные вопросы