Mikrokontrolery - Jak zacząć?

... czyli zbiór praktycznej wiedzy dot. mikrokontrolerów.

czwartek, 10 lutego 2011

Kurs języka C: Rzutowanie i promocja typów


Autor: Dondu

Kurs języka C: Spis treści

Podczas operowania na danych o różnych typach, kompilator przyjmuje pewne reguły tzw. promocji typów, czyli przyjęcia jaki typ ma mieć wynik całości lub części wyrażenia arytmetycznego. Zasady te są dość skomplikowane i dokładnie opisane w standardzie języka C.

By nie było niespodzianek w postaci "dziwnych" wyników obliczeń, masz do dyspozycji tzw. jawne rzutowanie. To poinformowanie kompilatora, jakiego typu jest dana liczba lub wynik wyrażenia arytmetycznego.

Na początek proste działanie na zmiennych typu int z wynikiem zmiennoprzecinkowym.

Przykład 1 (w kompilatorze)
  //deklaracja zmiennych
  int  a = 1;
  int  b = 3;
  float wynik;   //spodziewany wynik będzie zmiennoprzecinkowy 
  
  wynik = a / b;  //dzielenie arytmetyczne
  
  printf( "wynik = %f \n", wynik);  //%f bo wyświetlamy liczbę zmiennoprzecinkową

Ku naszemu zdziwieniu w wyniku działania programu otrzymujemy:

wynik = 0.000000

zamiast spodziewanego:

wynik = 0.333333


Dlaczego? 

Właśnie natknęliśmy się na algorytm promocji typów. Kompilator realizując dzielenie dwóch liczb całkowitych typu int, zgodnie z zasadami promocji typów przyjął, że wynikiem tej operacji ma być także int, przez co odrzucił część ułamkową wyniku (co z liczby 0.333333 dało 0) i taką wartość dopiero przypisał zmiennej wynik.


Co należy zrobić?

Mamy kilka rozwiązań tego problemu:


Sposób pierwszy

Zmieniamy jedną ze zmiennych dzielenia na typ float.

Przykład 2 (w kompilatorze)
float a = 1;


Sposób drugi

Wskazujemy kompilatorowi, że oczekujemy wyniku w postaci liczby typu float. To tak zwane "jawne rzutowanie typu", które polega na zamieszczeniu w nawiasach ( ) typu jaki chcemy, by kompilator przyjął dla danej zmiennej lub wyniku wyrażenia.

W naszym przypadku rzutowanie (float) ustawiamy po lewej stronie dzielenia, co oznacza dla kompilatora, że wynik dzielenia ma być typu float i dopiero przypisany do zmiennej wynik.

Ten sposób polecam jako przejrzysty w pisanym kodzie.

Przykład 3 (w kompilatorze)
wynik = (float) a / b;


Sposób trzeci

W przypadku gdy dzielimy zmienną typu int przez liczbę całkowitą przy takiej deklaracji:
  //deklaracja zmiennych
  int  a = 1;
  float wynik;   //spodziewany wynik będzie zmiennoprzecinkowy 
  
  wynik = a / 3;  //dzielenie arytmetyczne

także otrzymamy wynik:

wynik = 0.000000

W takim przypadku możemy kompilatorowi wskazać, że liczba 3 jest liczbą zmiennoprzecinkową dodając do niej część dziesiętną:

Przykład 4 (w kompilatorze)
  //deklaracja zmiennych
  int  a = 1;
  float wynik;   //spodziewany wynik będzie zmiennoprzecinkowy 
  
  wynik = a / 3.0;  //dzielenie arytmetyczne


Na koniec inny prosty przykład jawnego rzutowania typu.

Przykład 5 (w kompilatorze)
  
  //deklaracja zmiennej a typu double
  double a = 123.456789; 

  //wyświetlenie z rzutowaniem zmiennej a na typ int
  printf("%d", (int) a );

Wynik takiego rzutowania zobacz w kompilatorze CManiak.

I może jeszcze przykład bardziej skomplikowanego wyrażenia arytmetycznego.

Przykład 6 (w kompilatorze)
  
  //deklaracja zmiennych
  int  a=1, b=2, c=3, d=4;
  float wynik;          //spodziewany wynik będzie zmiennoprzecinkowy 
  
  wynik =  b/c * d + a;
  printf( "wynik 1 = %f \n", wynik);  

  wynik = (float) b/c * d + a;      //dodaliśmy rzutowanie na float
  printf( "wynik 2 = %f", wynik);  

Który wynik jest wynikiem oczekiwanym? Sprawdź w kompilatorze CManiak.


Podsumowanie:

Promocja i rzutowanie wbrew pozorom nie są utrudnieniem, lecz dają pełnię kontroli nad pisanym kodem i wynikami jego działania. Warto być konsekwentnym i jawnie rzutować w większości przypadków obliczeń na liczbach o różnych typach, by zawsze mieć pewność, że wynik będzie prawidłowy.

Kurs języka C: Spis treści

10
Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

3 komentarze:

  1. O ile dobrze widze interpretacja przykladu 3 w sposobie drugim jest nieprawidlowa. Zgodnie z kolejnoscia dzialan w c najpierw nastepuje rzutowanie a na typ float a dopiero potem jest obliczane dzielenie float przez int.

    OdpowiedzUsuń
    Odpowiedzi
    1. Przecież wszystko jest poprawnie opisane. Co wg ciebie jest nie tak?

      Usuń
    2. Zapis "rzutowanie (float) ustawiamy po lewej stronie dzielenia, co oznacza dla kompilatora, że wynik dzielenia ma być typu float..." wskazuje, ze najpierw realizowane jest dzielenie, a jego wynik jest rzutowany na float. Tymczasem w pierwszej kolejnosci rzutowana jest zmienna 'a', a dopieropotem nastepuje dzielenie

      Usuń

Działy
Działy dodatkowe
Inne
O blogu




Dzisiaj
--> za darmo!!! <--
1. USBasp
2. microBOARD M8


Napisz artykuł
--> i wygraj nagrodę. <--


Co nowego na blogu?
Śledź naszego Facebook-a



Co nowego na blogu?
Śledź nas na Google+

/* 20140911 Wyłączona prawa kolumna */
  • 00

    dni

  • 00

    godzin

  • :
  • 00

    minut

  • :
  • 00

    sekund

Nie czekaj do ostatniego dnia!
Jakość opisu projektu także jest istotna (pkt 9.2 regulaminu).

Sponsorzy:

Zapamiętaj ten artykuł w moim prywatnym spisie treści.