Skocz do zawartości

[C] Nieudana zamiana z systemu binarnego na decymalny


Pomocna odpowiedź

Jeśli chcesz typ string to string.h.

ALE nawet bez niego masz char * który działa podobnie ale szybciej. Jedyna wada jest taka że albo musisz zadeklarować stały rozmiar albo alokować ręcznie pamięć, ale tu to żaden problem.

Link to post
Share on other sites

Chumanista, typ string jest dostępny w C++, ale w czystym C go nie ma. Zostaje więc używanie char*. string.h zawiera funkcje działające na napisach właśnie typu char* (np. strlen, strcpy).

Jednak w przypadku takiej konwersji typ char* to idealny kandydat na parametr. Można wtedy zastosować schemat Hornera i mieć wreszcie klasyczny algorytm 😉

Jak chodzi o wywoływanie funkcji to nie trzeba przejmować się rozmiarami tablicy/napisu. Wystarczy wywołanie typu:

na10("00111111")

A jeśli nie chcemy poćwiczyć algorytmiki, zawsze można użyć funkcji strtol. Jest to standardowa funkcja z biblioteki C, więc znajdziemy ją w każdym kompilatorze (plik stdlib.h) http://www.cplusplus.com/reference/cstdlib/strtol/

Link to post
Share on other sites

Ale jeśli operujemy na stałych to cała zabawa nie ma sensu bo możemy po prostu napisać 0b00111111. Autorowi chodziło o transmisję danych o ile dobrze rozumiem, jakiś UART, takie rzeczy.

Masz rację, dawno nie pisałem w czystym C, nie pamiętam nawet kiedy używałem w nim char *.

Link to post
Share on other sites
Chumanista, typ string jest dostępny w C++, ale w czystym C go nie ma. Zostaje więc używanie char*. string.h zawiera funkcje działające na napisach właśnie typu char* (np. strlen, strcpy).

Jednak w przypadku takiej konwersji typ char* to idealny kandydat na parametr. Można wtedy zastosować schemat Hornera i mieć wreszcie klasyczny algorytm 😉

Jak chodzi o wywoływanie funkcji to nie trzeba przejmować się rozmiarami tablicy/napisu. Wystarczy wywołanie typu:

na10("00111111")

A jeśli nie chcemy poćwiczyć algorytmiki, zawsze można użyć funkcji strtol. Jest to standardowa funkcja z biblioteki C, więc znajdziemy ją w każdym kompilatorze (plik stdlib.h) http://www.cplusplus.com/reference/cstdlib/strtol/

Czyli jak rozumiem proponujesz takie rozwiązanie:

int na10(char liczba2[])
{
   int liczba10=0, mnoznik=1;
   for(int i=7;i>=0;i--)
   {
       liczba10 += (liczba2[i]-48) * mnoznik;
       mnoznik *= 2;
   }
   return liczba10;
}

Jutro sprawdzę czy to zadziała.

Link to post
Share on other sites

Już lepiej. Teraz można poprawić kilka rzeczy.

Po pierwsze - dlaczego zakładasz, że dane są 8 bitowe? Może warto zmienić program, żeby akceptował napisy dowolnej (powiedzmy do 32) długości? To wbrew pozorom kosmetyczna zmiana w kodzie 😉

Druga sprawa to mnożnik - pomyśl jak się go pozbyć. Kod będzie wydajniejszy i czytelniejszy.

I wreszcie ostatnie - zamiast mnożenia, spróbuj użyć przesuwania bitowego (<<). To znacznie "tańsza" operacja.

  • Pomogłeś! 1
Link to post
Share on other sites
Już lepiej. Teraz można poprawić kilka rzeczy.

Po pierwsze - dlaczego zakładasz, że dane są 8 bitowe? Może warto zmienić program, żeby akceptował napisy dowolnej (powiedzmy do 32) długości? To wbrew pozorom kosmetyczna zmiana w kodzie 😉

Druga sprawa to mnożnik - pomyśl jak się go pozbyć. Kod będzie wydajniejszy i czytelniejszy.

I wreszcie ostatnie - zamiast mnożenia, spróbuj użyć przesuwania bitowego (<<). To znacznie "tańsza" operacja.

Założeniem programu jest sterowanie 6 diodami podpiętymi do portów D mikrokontrolera Atmega8, więc teoretycznie wystarczy mi 6 znaków liczby binarnej. Jednak dla lepszego zobrazowania całego portu D zdecydowałem się na użycie 8 znaków. Więcej mi nie potrzeba. Gdyby jednak zaszła taka konieczność to wystarczy sprawdzić wielkość tablicy char'ów i powtórzyć pętlę dokładnie tyle razy.

Jeśli chodzi o mnożnik, to oczywiście można pokusić się o zastosowanie schematu Hornera czy choćby potęgowania, ale nie zmniejszy to ani złożoności obliczeniowej, ani pamięciowej tego algorytmu, ponieważ ilość operacji kluczowych będzie dokładnie taka sama. Zaintrygowałeś mnie jednak tym przesuwaniem bitowym - muszę trochę poczytać na ten temat.

Na chwilę obecną zostanę przy tej wersji programu, sprawdziłem i działa, więc dla potrzeb testowych mi wystarczy.

Jeszcze raz dziękuję bardzo za podsunięcie tego trefnego systemu ósemkowego, na to szybko bym nie wpadł 🙁

Problem rozwiązany więc temat uważam za zamknięty.

Link to post
Share on other sites

malum, trochę nie doceniasz możliwości optymalizacji tego programu. Oczywiście złożoność obliczeniowa pozostanie O(n), ale program może wykonywać 2 razy mniej mnożeń, czy też przesunięć bitowych.

Co więcej sprawdzanie wielkości tablicy jest bez sensu - wymaga przejścia przez całą tablicę, więc zajmie prawie tyle co całe przekształcenie.

Skoro zamykamy temat, to napiszę jak wygląda prawdziwie klasyczne rozwiązanie tego problemu:

int na10(const char* liczba2) 
{ 
   int wynik = 0; 
   while (*liczba2)
   { 
       wynik <<= 1;
       wynik += *liczba2 - '0'; 
       liczba2++;
   } 
   return wynik; 
} 
Link to post
Share on other sites

Chumanista, tak to jest jak się zmienia inny kod. W każdym razie teraz powinno być ok - nie sprawdzałem, więc jeśli są jakieś błędy to przepraszam. Chciałem pokazać jak algorytm wygląda. Inkrementacja "liczba2" jest jak najbardziej zamierzona. Tylko z tym się zagapiłem.

Teraz już powinno działać - oczywiście odejmować powinno się '0', a nie jak napisałem odruchowo '\0'.

Link to post
Share on other sites

Elvis, sprawdziłem działanie tego programu w DevC++ i nie bardzo działa. Po wykonaniu polecenia na10("00110011") otrzymałem 12291, a powinienem otrzymać 51. Czy kompilator C++ skompilował inaczej niż kompilator C?

Link to post
Share on other sites

Mój błąd - napisałem bez sprawdzania. Błąd był w przeliczaniu znaków na liczbę, odejmowałem '\0',czyli zero, a powinno być '0', czyli 48. To się nazywa głupi błąd 🙁

Link to post
Share on other sites
Mój błąd - napisałem bez sprawdzania. Błąd był w przeliczaniu znaków na liczbę, odejmowałem '\0',czyli zero, a powinno być '0', czyli 48. To się nazywa głupi błąd 🙁

Faktycznie, ja też przekleiłem mechanicznie bez przeanalizowania - teraz działa.

[ Dodano: 04-11-2015, 23:32 ]

int na10(const char* liczba2) 
{ 
   int wynik = 0; 
   while (*liczba2)
   { 
       wynik <<= 1;
       wynik += *liczba2 - '0'; 
       liczba2++;
   } 
   return wynik; 
} 

Szczerze powiem, że tej konstrukcji nie znałem, możesz trochę dokładniej opisać zastosowane polecenia?

Link to post
Share on other sites

Poczytaj o schemacie Hornera - to bardzo klasyczny / szkolny przykład. Przesunięcie bitowe w lewo to szybka metoda mnożenia przez potęgę 2 (w programie po prostu przez 2).

W sieci jest sporo informacji, np. http://iii-lo.tarnow.pl/informatyka/ladustrone.php?p1=informatyka&p2=41

Poza tym jest trochę magii w napisach C - są one zwykłymi tablicami, które można traktować jako wskaźniki, stąd arytmetyka na wskaźnikach. To kolejne fajne ćwiczenie podstaw C.

A program linijka po linijce:

    while (*liczba2) 
   {

Napisy w C kończą się zerem, więc pętla jest wykonywana, aż liczba2 będzie pustym napisem.

        wynik <<= 1; 

To co dotychczas obliczyliśmy mnożymy przez 2, to jest dokładnie schemat Hornera.

        wynik += *liczba2 - '0'; 

Teraz dodajemy najniższą cyfrę/bit.

        liczba2++;
   } 

To jest przejście do kolejnej litery w napisie.

    return wynik; 

Gotowe 🙂

Link to post
Share on other sites

Schemat Hornera znam, ale nie spotkałem się nigdy z konstrukcją

wynik <<= 1;

i szczerze mówiąc nadal nie bardzo rozumiem jak to działa, no i dlaczego w linijce

int na10(const char* liczba2)

char'a deklarujemy jako stałą?

Link to post
Share on other sites

Poczytaj o przesunięciach bitowych - to bardzo przydatna operacja. Mnożenie i dzielenie na mikrokontrolerach bywa bardzo czasochłonne. Natomiast każdy mikrokontroler potrafi wykonywać przesunięcia. Co więcej czasem mnożenie trzeba wykonać jako serię przesunięć i dodawań.

Często też przesunięć używa się do adresowania bitów, przykładowo popularne makro _BV() to właśnie przesunięcie bitowe:

#define _BV(bit) \
(1 << (bit)) 

Natomiast co do const, to jest to bardzo ważny parametr, chociaż często zapominany. Nie modyfikujesz otrzymanego napisu, więc zadeklarowanie go jako stałego nic nie kosztuje. Natomiast daje to kompilatorowi dodatkowe możliwości optymalizacji. Przede wszystkim parametr który przekazujesz, np. "0011111" może być przechowywany w niemodyfikowalnej pamięci, np. Flash - a mikrokontroler ma jej dużo więcej niż RAM.

Link to post
Share on other sites
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

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

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!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.