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
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ńPrzecież wszystko jest poprawnie opisane. Co wg ciebie jest nie tak?
Usuń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ń