Prostudujte si přednášku z jazyka C do tématu "Explicitní typová konverze" na stránce http://efis.tul.cz/~dana.nejedlova/ v odstavci "Programování I a II" Témata přednášky jsou ilustrována následujícími programy. Tyto programy si jeden po druhém pusťte a snažte se pochopit jejich chování. Implicitní typ literálů #include #define POCET_DES_MIST 20 int main(void) { double vysledek_double; float vysledek_float; vysledek_double = 10 / 3; printf("%.*f Celociselne deleni\n", POCET_DES_MIST, vysledek_double); printf("%.*f Celociselne deleni\n", POCET_DES_MIST, (double) (10 / 3)); printf("%.*f Hodnoty 10 i 3 jsou pretypovany na double. 10 explicitne a 3 implicitne\n", POCET_DES_MIST, (double) 10 / 3); printf("%.*f Deleni datoveho typu double\n", POCET_DES_MIST, 10.0 / 3.0); printf("%.*f Implicitni typova konverze 10.0f z float na double\n", POCET_DES_MIST, 10.0f / 3.0); vysledek_float = 10.0 / 3.0; printf("%.*f Deleni datoveho typu double a implicitní typova konverze vysledku na float\n", POCET_DES_MIST, vysledek_float); printf("%.*f Implicitni typova konverze 3 z int na float\n", POCET_DES_MIST, 10.0f / 3); printf("%.*f Deleni datoveho typu float\n", POCET_DES_MIST, 10.0f / 3.0f); printf("%d pocet bytu pro typ double\n", sizeof(double)); printf("%d pocet bytu pro typ float\n", sizeof(float)); printf("%d pocet bytu pro vyraz 10 / 3 = %d pocet bytu pro int\n", sizeof(10 / 3), sizeof(int)); printf("%d pocet bytu pro vyraz 10L / 3 = %d pocet bytu pro long int\n", sizeof(10L / 3), sizeof(long int)); printf("%d pocet bytu pro vyraz 10 / 3LL = %d pocet bytu pro long long int\n", sizeof(10 / 3LL), sizeof(long long int)); return 0; } Vysvětlení: Literál 1 je implicitně typu int. Literál 1.0 je implicitně typu double. Literál 1.0f je typu float. Výraz 10 / 3 má výsledek typu int. Výraz (double) (10 / 3) je výsledek celočíselného dělení explicitně přetypovaný na double, tedy při dělení se ztratila desetinná čast výsledku a konverze na double ji už neobnoví. Výraz (double) 10 / 3 má výsledek typu double, protože byl nejdříve 10 explicitně přetypován na double a potom byl 3 implicitně přetypován na double a potom teprve proběhlo dělení. Výpočty probíhají v datovém typu toho širšího ze dvou operandů, což je zajištěno implicitní typovou konverzí. Pořadí šířky vybraných datových typů operandů od nejužšího po nejširší: int, float, double. Přesněji platí, že současné běžné procesory provádějí výpočty výhradně v datovém typu double, viz například https://stackoverflow.com/questions/417568/float-vs-double-performance Výpočet tedy proběnhe na hardwaru pro datový typ double a jeho výsledek se vytiskne nebo vloží do proměné typu double zaokrouhlené dle datového typu, ve kterém proběhl výpočet. Pokud se výsledek vkládá do užšího datového typu, tak předtím zase proběhne implititní typová konverze. Datový typ float má méně desetinných míst a zabírá méně bajtů (bytů) než double, hodnoty typu float tedy jsou méně přesné než u typu double. Častá aplikace této teorie: Výpočet aritmetických průměrů z celočíselných hodnot, který má vyjít jako reálné číslo. Jazyk C nemá tabulku velikosti datových typů platnou pro všechny platformy. Místo toho se počet bajtů počítá operátorem sizeof(), pokud bychom to potřebovali například pro alokaci paměti, aby se mohl stejný název datového typu použít pro jeho různé hardwarové realizace (v různém počtu bajtů) a tím se zvýšila efektivita výpočtů a byla zajištěna přenositelnost zdrojového kódu na různé platformy. Operátor sizeof() se dá aplikovat na identifikátory datových typů, na identifikátory proměnných i na výrazy Přetečení Následující program se zacyklí, kdyz MAX > 127. Zacykledný program ukončíte zavřením okna terminálu vývojového prostředí. Příčinou zacyklení je to, že řídící proměnná cyklu je typu char a ten nedosáhne hodnoty vyšší než 127. Když je MAX rovno 127, tak poslední vytisknutá hodnota je 126, potom se j zvýší na 127 a přestane platit podmínka j < MAX a tím cyklus skončí. #include #define MAX 128 int main(void) { char i; for (i = 0; i < MAX; i++) { printf("%d ", i); } return 0; } Neplatí, že by se výše uvedený program zacyklil na všech platformách, protože na některých z nich je datový typ char implicitně neznaménkový, což je to samé, jak kdyby byl deklarován jako unsigned char. Do znaménkového typu char se vejde hodnota 128 a tak se program zastaví. Zacyklil by se až pro MAX rovné 256. #include #define MAX 128 int main(void) { unsigned char i; for (i = 0; i < MAX; i++) { printf("%d ", i); } return 0; } Následující program ilustruje jev přetečení pomocí tisku aritmetické posloupnosti. Přetečení znamená, že místo toho, aby se tiskla řada od 0 do MAX - 1, tak se vytiskne část řady jako záporná čísla díky tomu, že hodnoty se interpretují pomocí pravidla pro dvojkový doplněk, viz "Přednáška - Čísla v počítači" v odstavci "Počítače I" na stránce http://efis.tul.cz/~dana.nejedlova/ Když bude proměnná j typu unsigned char, tak přeteče až po vytisknutí hodnoty 255. Když bude MAX rovna 32768, nebo na jakékoliv platformě hodnota konstanty v knihovně limits.h SHRT_MAX + 1, tak se program zacyklí, protože bude přetékat i řídící proměnná cyklu. Obsah knihovny limits.h si lze prohlédnout po kliknutí pravým tlačítkem myši na její identifkátor ve vývojovém prostředí Code::Blocks a kliknutí na volbu Open #include file. Datový typ short int je zde použit proto, aby se program rychle skončil, když bude MAX rovno SHRT_MAX. Typičtěji se pro řidící proměnnou cyklu používá datový typ int. #include #include #define MAX 32767 int main(void) { short int i; char j = 0; for (i = 0; i < MAX; i++) { printf("%d ", j++); } return 0; } Ztráta přesnosti Ztráta přesnosti je jev společný pro reálné datové typy, tedy float a double a bude vysvětlen na programu počítajícím kořeny kvadratické rovnice axx + bx + c = 0. #include #include // kvuli funkci sqrt() #define POCET_DES_MIST 20 int main(void) { double a, b, c, diskriminant; printf("Program pocitajici x pro kvadratickou rovnici a * x * x + b * x + c = 0\n"); printf("Zadej a: "); scanf("%lf", &a); printf("Zadej b: "); scanf("%lf", &b); printf("Zadej c: "); scanf("%lf", &c); diskriminant = b * b - 4 * a * c; if (diskriminant < 0) { printf("Rovnice ma reseni v oboru komplexnich cisel.\n"); printf("x1 = %.*f + %.*fi\n", POCET_DES_MIST, -b / (2 * a), POCET_DES_MIST, sqrt(-diskriminant) / (2 * a)); printf("x2 = %.*f - %.*fi\n", POCET_DES_MIST, -b / (2 * a), POCET_DES_MIST, sqrt(-diskriminant) / (2 * a)); } else if (diskriminant > 0) { printf("Rovnice ma reseni v oboru realnych cisel.\n"); printf("x1 = %.*f\n", POCET_DES_MIST, (-b + sqrt(diskriminant)) / (2 * a)); printf("x2 = %.*f\n", POCET_DES_MIST, (-b - sqrt(diskriminant)) / (2 * a)); } else { printf("Rovnice ma jediny koren.\n"); printf("x = %.*f\n", POCET_DES_MIST, -b / (2 * a)); } return 0; } První verze programu počítá správně výsledek pro vstup a = 1 b = 2 c = -1 kde jsou výsledkem dvě různá řešení. První verze programu počítá správně výsledek pro vstup a = 1 b = 2 c = 1 kde je výsledkem jediné řešení. První verze programu počítá správně výsledek pro vstup a = 1 b = -0.02 c = 0.00010001 kde je výsledkem jsou komplexní čísla. Ale vstup a = 1 b = -0.2 c = 0.01 má mít jediné řešení, ale vyjde jako 2 reálná čísla. A vstup a = 1 b = -0.02 c = 0.0001 má mít jediné řešení, ale vyjde jako 2 komplexní čísla. Druhá verze programu tyto problémy řeší pomocí konstanty PRESNOST a správným pořadím větví, tedy musíme nejdřív testovat na rovnost nule a ostatní dvě větve mohou být potom v libovolném pořadí. Konstanta PRESNOST je též vysvětlena na snímku "IEEE 754 a aritmetické operace" v přednášce pod odkazem "Přednáška - Čísla v počítači" v odstavci "Počítače I" na stránce http://efis.tul.cz/~dana.nejedlova/ #include #include // kvuli funkcim sqrt() a fabs() #define POCET_DES_MIST 20 #define PRESNOST 1e-10 int main(void) { double a, b, c, diskriminant; printf("Program pocitajici x pro kvadratickou rovnici a * x * x + b * x + c = 0\n"); printf("Zadej a: "); scanf("%lf", &a); printf("Zadej b: "); scanf("%lf", &b); printf("Zadej c: "); scanf("%lf", &c); diskriminant = b * b - 4 * a * c; if (fabs(diskriminant) < PRESNOST) { printf("Rovnice ma jediny koren.\n"); printf("x = %.*f\n", POCET_DES_MIST, -b / (2 * a)); } else if (diskriminant > 0) { printf("Rovnice ma reseni v oboru realnych cisel.\n"); printf("x1 = %.*f\n", POCET_DES_MIST, (-b + sqrt(diskriminant)) / (2 * a)); printf("x2 = %.*f\n", POCET_DES_MIST, (-b - sqrt(diskriminant)) / (2 * a)); } else if (diskriminant < 0) { printf("Rovnice ma reseni v oboru komplexnich cisel.\n"); printf("x1 = %.*f + %.*fi\n", POCET_DES_MIST, -b / (2 * a), POCET_DES_MIST, sqrt(-diskriminant) / (2 * a)); printf("x2 = %.*f - %.*fi\n", POCET_DES_MIST, -b / (2 * a), POCET_DES_MIST, sqrt(-diskriminant) / (2 * a)); } return 0; } Konstanta PRESNOST nesmí být moc malá, například 1e-15 (1 krát 10 na -15 = 0.000000000000001) už je příliš málo, protože datový typ double může mít maximálně 15 platných číslic (52 bitů v mantise krát log 2 o základu 10), viz kapitola "1.3 Numerická data s pohyblivou řádovou čárkou neboli reálná čísla" v přednášce pod odkazem "Přednášky" v odstavci "Počítače I" na stránce http://efis.tul.cz/~dana.nejedlova/ Potom tedy některá zadání, například a = 1 b = -0.02 c = 0.00010000000000000001 počítač správně nevyřeší (vyjde jediný kořen, ale mají to být 2 komplexní čísla), protože datový typ double je omezen.