2009-09-11 14 views
7

Jako ćwiczenie, chciałbym napisać makro, które mówi mi, czy jest podpisana zmienna całkowita. To jest to, co mam do tej pory i mam wyniki, których się spodziewam, jeśli spróbuję tego na zmiennej char z gcc -fsigned-char lub -funsigned-char.Jak sprawdzić, czy zmienna całkowita C jest podpisana?

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

Czy to przenośne? Czy istnieje sposób na zrobienie tego bez niszczenia wartości zmiennej?

+0

To jest ciekawy problem, ale jestem o wiele bardziej zaintrygowany tym, jakie jest zamierzone wykorzystanie tych informacji jest. Czy masz szansę na dzielenie się? –

+0

To dlatego C++ ma RTTI. :) –

+2

@jeffamaphone: W rzeczywistości jest to miejsce, w którym szablony świecą w C++. – sbi

Odpowiedz

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

nie zmienia wartości V. Trzecia próba obsługuje przypadek, w którym V == 0.

Na moim kompilator (gcc/cygwin) to działa na int i long ale nie dla char lub short.

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

wykonuje również zadanie w dwóch testach.

+0

Najlepsze, jakie widziałem. Przenośny, zgodny z normą, dokładny, o ile widzę. –

+1

Nie rozróżnia podpisanych/niepisanych skrótów i znaków. Te typy są promowane do int podczas oceny wyrażenia < == >. – mob

+0

Jeśli chcesz, aby działało to na 'short' i' char', prawdopodobnie mógłbyś rzucić zmienną na 'char' przed jej użyciem. To powinno obsłużyć większość problemów z przepełnieniem. Myślę ... –

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

Bez niszczenia wartości zmiennej. Ale nie działa dla wartości 0.

Co o:

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

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

+0

Tak, nie powinienem mieć C & Pd to zbędne rzeczy ;-) – ypnos

+0

@plinth, zapomniałeś o "ekstra" parenach wokół "V", które czynią go bezpiecznym od szalonych ekspansji makr – rmeador

5

Jeśli używasz GCC można użyć słowa kluczowego typeof aby nie nadpisać wartość:

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

ta tworzy zmienną tymczasową, _V, że ma ten sam typ co V.

Co do przenośności, nie wiem. Będzie działał na maszynie dwóch komplementów (a.k.a. wszystko, co twój kod kiedykolwiek uruchomi z dużym prawdopodobieństwem), i wierzę, że będzie działać również na maszynach komplementu i znaku-i-magnitudo. Na marginesie, jeśli używasz typeof, możesz odrzucić -1 do typeof (V), aby było bezpieczniejsze (tj. Mniej prawdopodobne jest wyzwolenie ostrzeżeń).

+0

W C++ jest gwarantowane działanie standardu, niezależnie od reprezentacji całkowitej (dla n bitów wartość wynosi 2^n - 1). Nie mam pod ręką standardu C (żadnego z nich). –

+0

Ja też nie, ale pamiętam, że czytanie na Wikipedii (źródło wszystkich rzeczy, prawda: P), że standard C dopuszcza trzy reprezentacje, które wymieniłem. Nie, żeby ktokolwiek już z nich korzystał ... –

-1

Charakterystyczną cechą matematyki podpisanej/niepodpisanej jest to, że po przesunięciu prawej strony podpisanego numeru zostanie skopiowany najbardziej znaczący bit. Podczas zmiany numeru niepodpisany, nowe bity 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 

Więc w zasadzie, to makro używa wyrażenia warunkowego w celu ustalenia, czy wysoki bit numer jest ustawiony. Jeśli tak nie jest, makro ustawia je bitowo, negując liczbę. Nie możemy wykonać arytmetycznej negacji, ponieważ -0 == 0. Następnie przesuwa się w prawo o 1 bit i sprawdza, czy wystąpiło przedłużenie znaku.

Zakłada to arytmetykę uzupełnień 2, ale zwykle jest to bezpieczne założenie.

+0

Czy masz źródło dla tych założeń dotyczących zachowania zmian bitowych? –

+0

Standard C99 (sekcja 6.5.7) mówi, że prawe przesunięcie podpisanej wartości ujemnej jest zdefiniowane przez implementację. Moją interpretacją jest to, że pojawi się rozszerzenie znaku na maszynie z dodatkiem 2. Ponieważ C nie jest specyficzne dla architektur uzupełnień 2, nie wyjdą i powiedzą to. –

+0

Byłbyś bardziej PC używając 'CHAR_BITS' zamiast magicznego' 8'. (Tak, wiem, jest nas niewielu z nas _nie pracujemy na maszynach, w których bajt jest 8-bitowy.). – sbi

1

To proste rozwiązanie nie ma żadnych skutków ubocznych, w tym korzyści odnoszących się tylko do v raz (co jest ważne w makro). Używamy gcc rozszerzenie „typeof”, aby uzyskać typ v, a następnie rzucić -1 do tego typu:

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

To < = zamiast po prostu < uniknąć ostrzeżenia kompilatora dla niektórych przypadkach (gdy włączone).

0

Dlaczego, na Boga, potrzebujesz makro?Szablony doskonale nadają się do tego:

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

Który zadziała po wyjęciu z pudełka dla wszystkich podstawowych typów integralnych. W czasie kompilacji przestanie działać na wskaźnikach, których wersja używająca jedynie odejmowania i porównywania prawdopodobnie nie będzie.

EDIT: Oops, kwestia wymaga C. Mimo, szablony są miły sposób: P

0

Inne podejście do wszystkich "czynią go" odpowiedzi negatywnych:

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

ten sposób nie ma potrzeby posiadania specjalnych przypadków dla różnych wartości V, ponieważ ∀ V ∈ ℤ, V^V = 0.

Powiązane problemy