ciscoc Napisano Wrzesień 10, 2012 Udostępnij Napisano Wrzesień 10, 2012 Co to znaczy że zmienna jest typu całkowitego znakowego oraz bez znakowego? [ Dodano: 10-09-2012, 21:57 ] podajcie jakiś przykład Cytuj Link do komentarza Share on other sites More sharing options...
mactro Wrzesień 10, 2012 Udostępnij Wrzesień 10, 2012 Znaczy to, że może reprezentować liczby ujemne, lub nie może. Przykładowo: char ma zakres od -128 do 127, a unsigned char ma zakres od 0 do 255. Cytuj Link do komentarza Share on other sites More sharing options...
MirekCz Wrzesień 11, 2012 Udostępnij Wrzesień 11, 2012 Nie bity ujemne, a liczby ujemne 😉 Generalnie jak masz zmienną signed (czyli ze znakiem) to jej najwyższy bit (MSB - most significant bit) reprezentuje liczbę minus coś. Jak masz zmienną bez znaku to najwyższy bit reprezentuje liczbę dodatnią. Np dla 8 bitowych zmiennych posczzególne bity (od najmłodszego do najstarszego) reprezentują dla unsigned char: bit->liczba 1=1 2=2 3=4 4=8 5=16 6=32 7=64 8=128 a dla signed char: 1=1 2=2 3=4 4=8 5=16 6=32 7=64 8=-128 Dlatego unsigned char reprezentuje liczby od 0 do 255, a signed char od -128 (10000000b) do 127 (01111111b) Cytuj Link do komentarza Share on other sites More sharing options...
Sabre Wrzesień 11, 2012 Udostępnij Wrzesień 11, 2012 Dlatego unsigned char reprezentuje liczby od 0 do 255, a signed char od -128 (10000000b) do 127 (01111111b) Nie znam C i chyba na szczęście w tym przypadku, bo jak zapisać -127 jako signed char, skoro najstarszy bit przechowuje jednocześnie informację o znaku jak i o wartości liczby. Albo źle to opisałeś albo ten zapis jest po prostu idiotyczny. Cytuj Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Produkcja i montaż PCB - wybierz sprawdzone PCBWay! • Darmowe płytki dla studentów i projektów non-profit • Tylko 5$ za 10 prototypów PCB w 24 godziny • Usługa projektowania PCB na zlecenie • Montaż PCB od 30$ + bezpłatna dostawa i szablony • Darmowe narzędzie do podglądu plików Gerber Zobacz również » Film z fabryki PCBWay
koval_blazej Wrzesień 11, 2012 Udostępnij Wrzesień 11, 2012 Zgodnie z tym opisem 10000001 da -127 Cytuj Link do komentarza Share on other sites More sharing options...
Sabre Wrzesień 11, 2012 Udostępnij Wrzesień 11, 2012 koval_blazej, tak to może być zapisane -1, -127 to byłoby 11111111b. EDIT: A jednak widzę, że nie mam racji, ale to wina zasugerowania się zapisem: Dlatego unsigned char reprezentuje liczby od 0 do 255, a signed char od -128 (10000000b) do 127 (01111111b) Bo powinien on wyglądać tak, że jest to liczba -128 i zakres od 0 do 127, a nie liczby od -128 do 127. Już doczytałem i wiem, że liczby ujemne zapisuje się jako negację liczby dodatniej plus 1. Czyli będzie tak jak napisał koval_blazej, -127 to będzie -128+1 czyli właśnie 10000001b a -1 to będzie -128 plus 127 czyli 11111111b. Jak dobrze, że jest Bascom 😃 Cytuj Link do komentarza Share on other sites More sharing options...
kling Wrzesień 11, 2012 Udostępnij Wrzesień 11, 2012 To o czym mówicie to kod U2. Aktualnie jest to standard kodowania liczb calkowitych w procesorach. Tutaj jest fajny konwerter liczb '10' na binarne w kodzie U2. Może pomoże to autorowi tematu zrozumieć różnice;) Cytuj Link do komentarza Share on other sites More sharing options...
marek1707 Wrzesień 11, 2012 Udostępnij Wrzesień 11, 2012 To, czy zadeklarowaliśmy zmienną ze znakiem czy bez przesądza jedynie o sposobie interpretacji danej kombinacji bitów przez arytmometr procesora a ściślej przez instrukcje badania warunków. Kod uzupełnieniowy do 2 (warto o nim poczytać), jakim posługują się dzisiaj chyba wszystkie procesory ma to do siebie, że ten sam układ cyfrowy może prawidłowo dodawać i odejmować liczby nic nie wiedząc o ich "znakowości". Wynik będzie zawsze prawidłowy pod warunkiem, że nie przekroczymy zakresów ale to chyba oczywiste. Tak więc mając zmienną typu unsigned char (czyli jeden bajt bez znaku) i robiąc takie podstawienie: a = 200; wpisujemy pewną kombinację bitów (11001000) do pamięci. Chcąc coś do niej dodać możemy napisać: a += 10; w wyniku czego arytmometr procesora wykona rozkaz zwykłego dodawania: 11001000 + 00001010 = 11010010 wcale nie wiedząc jaki jest typ tych liczb. Wynik jest oczywiście prawidłowy: 205 dziesiętnie. Gdyby natomiast nasza zmienna była typu signed char, po pierwsze nie moglibyśmy wprost podstawić do niej liczby 200 ale to już jest cecha kompilatora. Ten wiedząc, że na 1 bajcie mozna ze znakiem zakodować tylko liczby z zakresu -128..+127 będzie marudził na takie jawne złamanie zasad: a = 200 i będzie to błąd kompilacji. Możemy natomiast bezkarnie wpisać tam taką liczbę (co było z kolei poprzednio zabronione): a = -56; Jeśli ktoś teraz zamieni to sobie na obraz bitowy wg zasad kodu U2 (tak go w skrócie nazywam) to zobaczy, że kombinacja bitów jest dokładnie taka sama jak poprzednio liczby 200: 11001000 I jeśli teraz powtórzymy operację: a += 5 to procesor wykona dokładnie takie samo dodawanie: 11001000 + 00001010 = 11010010 w wyniku czego dostaniemy dokładnie taką samą kombinację bitów w pamięci wyniku ale teraz, ponieważ interpretujemy ją jako liczbę ze znakiem to rozumiemy ją jako -51. Całym "sprytem" kodu U2 jest sposób konwersji liczb dodatnich na ujemne i odwrotnie. Aby jedną zamienić na drugą (to jest symetryczne i działa w obie strony tak samo) wystarczy odwrócić stany wszystkich bitów na przeciwne i dodać 1. Przykład: +1 czyli 00000001 po zamianie na -1 daje 11111111 +100 czyli 01100100 po zamianie na -100 daje 10011100 +127 czyli 01111111 po zamianie na -127 daje 10000001 ale już +128 czyli 10000000 po zamianie na -128 daje nam 10000000 czyli zgodnie z tą interpretacją jest to znów +128 - tu już zakres się skończył.. Jak łatwo zauważyć najstarszy bit jest ustawiony gdy liczba jest ujemna ale wcale nie "waży" on -128. Taki chwyt jak w dodawaniu i odejmowaniu kodu U2 nie działa jednak podczas operacji mnożenia i dzielenia. Małe procesory wyposażone są najczęściej w układy mnożące tylko liczby dodatnie więc mnożenie liczb ze znakiem musi być zastąpione sekwencją policzenia modułów z obu argumentów, pomnożenia ich jako liczb dodatnich a potem ew. zmianą znaku wyniku jeśli znaki argumentów były różne - to trwa troszkę dłużej (jakieś 2 razy?) niż mnożenie liczb bez znaku i warto o tym pamiętać. No i teraz badanie warunków - procesor ma inne instrukcje do badania większości/mniejszości liczb ze znakiem i bez. Dlatego też kompilator umieszcza inne intstrukcje gdy w if-ie porównujemy liczby bez znaku i ze znakiem. Porównywanie sprowadza się jak wiadomo do odjęcia jednej liczby od drugiej i sprawdzeniu znaku wyniku ale tutaj już symetrii nie ma. Proponuję sobie to prześledzić na kilku przykładach na papierze 🙂 BTW: Pisząc w C mamy jeszcze jedną "warstwę" pośrednią - kompilator, który próbuje nas chronić przed trywialnymi błędami. Dlatego też nie pozwoli nam założyć pętli for korzystającej np. ze zmiennej bajtowej ze znakiem w ten sposób: for(n=0; n<200; n++) ponieważ "wie", że ta zmienna takiej wartości nigdy nie osiągnie. Gdyby zmienna była bez znaku, linia kodu skompilowałaby się prawidłowo. Oczywiście jest to trochę sztuczne, bo przecież instrukcja dodająca 1 w każdym obrocie pętli w końcu dostałaby w wyniku kombinację 11001000 czyli 200 ale tym samym zostałaby złamana idea C rozróżniania zakresów liczb ze znakiem i bez. EDIT: Kling, pisaliśmy razem.. 🙂 Cytuj Link do komentarza Share on other sites More sharing options...
MirekCz Wrzesień 11, 2012 Udostępnij Wrzesień 11, 2012 Nie mam racji, ale to wina zasugerowania się zapisem: Dlatego unsigned char reprezentuje liczby od 0 do 255, a signed char od -128 (10000000b) do 127 (01111111b) Bo powinien on wyglądać tak, że jest to liczba -128 i zakres od 0 do 127, a nie liczby od -128 do 127. Tak jak podałem poszczególne bity oznaczają poszczególne liczby, które potem się sumują. Jak masz 10000001b to masz = -128 + 1 = -127. Jak masz 11000000b to masz = -128 + 64 = -64. Jak masz 11111111b to masz = -128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = -1 Wszystko jest proste 🙂 Cytuj Link do komentarza Share on other sites More sharing options...
Marooned Wrzesień 11, 2012 Udostępnij Wrzesień 11, 2012 marek1707 przytoczył najważniejszą kwestię, która pomaga w zrozumieniu "jak to wszystko działa". Zmienne typu signed czy unsigned nie różnią się absolutnie niczym dla procesora, w pamięci są zapisane identycznie. To jedynie informacja dla kompilatora jakich instrukcji skoku użyć po porównaniu (które jest zwyczajnym odjęciem od siebie 2 liczb). Nie pamiętam nazw mnemoników dla Atmeg, ale dość podobnie jest dla x86, gdzie dla porównań bez znaku mamy (upraszczając) dwa skoki: JA, JB (Jump if Above, Jump if Below) a dla porównań ze znakiem mamy: JG, JL (Jump if Greater, Jump if Less). Instrukcje te badają inne bity rejestru stanu, który jest ustawiany przez większość innych instrukcji, w tym instrukcję porównania (jak wspomniano, identycznej z instrukcją odejmowania gubiącej jedynie wynik operacji). Dlatego właśnie nie przepadam za Bascomami etc. Programista nie wie co się dzieje "pod kapeluszem", często nie rozumiejąc kodu oraz sposobu, w jaki procesor go wykonuje. Przy miganiu LEDem to może nie ma znaczenia, ale jeśli trzeba napisać uber wydajny kod, to znaczenie rozumienia kodu jest spore. Fakt, że teraz za nas wiele robi kompilator, ale wtedy liczymy na to, że ktoś przewidział daną sytuację. Dla prostego przykładu: badanie parzystości liczby. 98% programistów napisze if (a % 2) {nieparzysta;} else {parzysta;} a ja przywykłem do pisania if (a & 1) {nieparzysta;} else {parzysta;} Zakładając, że kompilator nie zoptymalizuje kodu i zostawi nam rzeczywiście dzielenie to zamiast jednego taktu procesora na operacji bitowej spędzamy kilka(naście) na dzieleniu by otrzymać dokładnie ten sam efekt... Cytuj Link do komentarza Share on other sites More sharing options...
mactro Wrzesień 11, 2012 Udostępnij Wrzesień 11, 2012 Marooned, tylko trzeba pamiętać, że nie zawsze warto poświęcać przejrzystość kodu na rzecz jego wydajności. W Twoich przykładach, w pierwszym od razu widać, że badana jest parzystość, a w drugim trzeba się już chwilę zastanowić. Nieraz faktycznie można zastosować różne fajne sztuczki, które przyspieszą działanie programu, ale jeśli nie są to fragmenty wykonywane miliony razy, to IMHO lepiej jest zostać przy formie bardziej czytelnej. Cytuj Link do komentarza Share on other sites More sharing options...
Marooned Wrzesień 11, 2012 Udostępnij Wrzesień 11, 2012 Niby tak, ale to zależy też od tego kto czyta kod. Dla niektórych oba warianty są jasne, a inny będzie dumał "co to za procencik". Zdarzało mi się pisać taki kod, że współtwórca projektu robił wielkie oczy. Wtedy oczywiście stosowny komentarz przy kodzie wyjaśniał elegancko sprawę. No ale chyba zbyt mocno odbiegłem od meritum, posypuję głowę popiołem. Cytuj Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
Dołącz do dyskusji, napisz odpowiedź!
Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!