Předvánoční slevová akce Předvánoční slevová akce
Využij předvánočních slev a získej od nás 20 % bodů zdarma! Více zde

Lekce 11 - Matematické funkce v jazyce C

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Vícerozměrná pole v jazyce C, jsme si představili vícerozměrná pole. V dnešním tutoriálu kurzu základů jazyka C se podíváme na standardní knihovnu math.h. Ta poskytuje velké množství funkcí pro řešení obvyklých matematických problémů, ty nejdůležitější z nich si uvedeme.

fmin(), fmax(), fdim()

Začněme s tím jednodušším :) Všechny funkce přijímají jako parametr dvě čísla typu double. Funkce fmin() vrátí to menší, funkce fmax() to větší z nich. Funkce fdim() vrátí x - y, je-li x > y. V ostatních případech vrací 0.

round(), ceil(), floor() a trunc()

Všechny funkce se týkají zaokrouhlování a přijímají parametry typu double. Návratová hodnota je pro všechny funkce rovněž typu double. round() vrací zaokrouhlené číslo tak, jak to známe ze školy (od 0.5 nahoru, jinak dolů). ceil() zaokrouhlí vždy nahoru a floor() vždy dolů. trunc() nezaokrouhluje, pouze odtrhne desetinnou část.

round() budeme jistě potřebovat často, další funkce jsem často použil například když jsem zjišťoval počet položek na stránce (např. nějaké tabulky v konzoli). Máme-li 33 položek a na stránce jich je vypsáno 10, budou matematicky zabírat 3.3 stránek. Výsledek musíme zaokrouhlit nahoru, protože v reálu stránky budou samozřejmě 4.

Pokud vás napadlo, že floor() a trunc() dělají to samé, chovají se jinak u záporných čísel. Tehdy floor() zaokrouhlí na číslo více do mínusu, trunc() zaokrouhlí vždy k nule.

Zaokrouhlení desetinného čísla a jeho uložení do proměnné typu int tedy provedeme následujícím způsobem:

double d = 2.72;
int a = (int)round(d);

Přetypování na int je nutné, jelikož round() vrací sice celé číslo, ale stále uložené v typu double a to kvůli tomu, aby všechny matematické funkce měly stejné rozhraní. Proto nebudu u zbylých funkcí typy uvádět - vždy bude double.

abs() a signbit()

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

abs() vrátí absolutní hodnotu parametru a signbit() vrátí 1, je-li číslo záporné, ve zbylých případech vrací 0. Funkce signbit() není podporovaná ve všech kompilerech, proto je možné, že ji nemusíte mít dostupnou.

sin(), cos(), tan()

Klasické goniometrické funkce přijímají jako parametr úhel v radiánech, nikoli ve stupních. Pro konverzi stupňů na radiány stupně vynásobíme hodnotou * (M_PI / 180).

acos(), asin(), atan()

Opět klasické cyklometrické funkce (arkus funkce), které podle hodnoty goniometrické funkce vrátí daný úhel. Jedná se o inverzní funkce k sin(), cos() a tan(). Parametrem je hodnota úhlu, výstupem úhel v radiánech. Pokud si přejeme mít úhel ve stupních, vynásobíme radiány hodnotou * (180 / M_PI).

pow() a sqrt()

pow() přijímá dva parametry, první je základ mocniny a druhý exponent. Pokud bychom tedy chtěli vypočítat např. 23, kód by byl následující:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(int argc, char** argv)
{
    double vysledek = pow(2,3);
    printf("%lf", vysledek);
    return (EXIT_SUCCESS);
}

sqrt() je zkratka ze SQuare RooT a vrací druhou odmocninu z daného čísla.

exp(), log(), log10()

Funkce exp() vrací Eulerovo číslo, umocněné na daný exponent. Funkce log() vrací přirozený logaritmus daného čísla. log10() vrací dekadický logaritmus daného čísla.

V seznamu metod nápadně chybí libovolná odmocnina. My ji však dokážeme spočítat i na základě funkcí, které knihovna poskytuje.

Víme, že platí: 3. odm. z 8 = 8^(1/3). Můžeme tedy napsat:

double vysledek = pow(8, (1.0/3.0));
printf("%lf", vysledek);

Je velmi důležité, abychom při dělení napsali alespoň jedno číslo s desetinnou tečkou, jinak bude C předpokládat celočíselné dělení a výsledkem by v tomto případě bylo 8^0 = 1.

Dělení

Programovací jazyky se často odlišují tím, jak v nich funguje dělení čísel. Tuto problematiku je nutné dobře znát, abyste nebyli překvapeni. Napišme si jednoduchý program:

int a = 5 / 2;
double b = 5 / 2;
double c = 5.0 / 2;
double d = 5 / 2.0;
double e = 5.0 / 2.0;
int f = 5 / 2.0;
printf("a=%d b=%f c=%f d=%f e=%f f=%d", a, b, c, d, e, f);

V kódu několikrát dělíme 5 / 2, což je matematicky 2.5. Jistě ale tušíte, že výsledek nebude ve všech případech stejný. Troufnete si tipnout, co kdy vyjde? Zkuste to :)

Konzolová aplikace
a=2 b=2.000000 c=2.500000 d=2.500000 e=2.500000 f=2

Vidíme, že výsledek dělení je někdy celočíselný a někdy reálný. Přitom nezáleží pouze na datovém typu proměnné, do které výsledek ukládáme, ale na datovém typu čísel, které dělíme. Pokud je jedno z čísel desetinné, je výsledek vždy desetinné číslo. 2 celá čísla vždy vrátí celé číslo, dejte si na to pozor, např. když budete počítat průměr. Pro desetinný výsledek je nutné alespoň jednu proměnnou přetypovat na desetinné číslo.

int soucet = 10;
int pocet = 4;
double prumer = (double)soucet / (double)pocet;

Pozn.: Např. v jazyce PHP je výsledek dělení vždy desetinný. Až budete dělit v jiném programovacím jazyce než je C, zjistěte si, jak dělení funguje než jej použijete.

Zbytek po celočíselném dělení

V našich aplikacích můžeme často potřebovat zbytek po celočíselném dělení (tzv. modulo). U našeho příkladu 5 / 2 je celočíselný výsledek 2 a modulo 1 (zbytek). Modulo se často používá pro zjištění, zda je číslo sudé (zbytek po dělení 2 je 0) nebo když chcete zjistit odchylku vaší pozice od nějaké čtvercové sítě.

V céčku a obecně v céčkových jazycích (tzv. C-like jazyky) zapíšeme modulo jako %:

printf("Zbytek po deleni 5/2 je %d", 5 % 2);

Tak to bychom měli. V příští lekci, Funkce v jazyce C, si uvedeme funkce, které jsou velmi důležité - pomohou nám rozdělit program do více logických celků.


 

 

Článek pro vás napsal Patrik Valkovič
Avatar
Jak se ti líbí článek?
2 hlasů
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Předchozí článek
Vícerozměrná pole v jazyce C
Všechny články v sekci
Základní konstrukce jazyka C
Miniatura
Následující článek
Cvičení k 10. a 11. lekci Céčka
Aktivity (22)

 

 

Komentáře

Avatar
lastp
Redaktor
Avatar
lastp:16.7.2016 11:51

Funkce fdim vrací nulu, pokud první parametr je menší než druhý.

 
Odpovědět
16.7.2016 11:51
Avatar
Patrik Valkovič
Šéfredaktor
Avatar
Odpovídá na lastp
Patrik Valkovič:16.7.2016 12:25

Díky opraveno.

Odpovědět
16.7.2016 12:25
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
Vlasta Mozny
Člen
Avatar
Vlasta Mozny:22.1.2017 19:37
#include <iostream>
int Soucet(int x, int y)
{
    std::cout << "Loading ..... Uvedl jsi tyto 2 cisla ke scitani. " << x << "+" << y << "=???????";
    return (x+y);
}

int main()
{
    using std::cout;
    using std::cin;


    cout << "                       Vitam vas v Kalkulace v1.0!\n";
    int  a, b, c;
    cout << "\n";
    cout << "Napis nejake 2 cisla co te napadnou ke scitani s mezerou:";
    cin >> a ;
    cin >> b ;
    cout << "\nNahrava se program Scitani  .. O_O .. \n";
    c=Soucet (a,b);
    cout << "\nLoading ..... Mam Pro tebe tve cislicka ke scihnuti!!!\n";
    cout << "\n";
    cout << "Vysledek  je pokud se teda nemylim a to se opravdu nemylim!!! " << c;
    cout << "\n";
    cout << "\nKonec programu kalkulacky kterou upravil Remsa Vlastimil a Robutek Mark.";
    cout << "\nA ted se s vamy loucime :] Mame jine veci na praci ....\n";
    cout << "\n";
    cout << "                       Bye (O_O)  (^.^) Bye";
    cout << "\n";
    return 0;
}

Tu je moje kalkulacka pro pobaveni
Odpovědět
22.1.2017 19:37
„Pokud chcete, aby vás měli za lháře, vždy říkejte pravdu.“
Avatar
jan široký
Člen
Avatar
jan široký:3.10.2018 13:35

ahoj. funkce abs() pracuje s intem. https://www.tutorialspoint.com/…tion_abs.htm

 
Odpovědět
3.10.2018 13:35
Avatar
jan široký
Člen
Avatar
jan široký:3.10.2018 13:45

Jedná se o inverzní funkce k sin(), cos() a tan(). Parametrem je hodnota úhlu, výstupem úhel v radiánech. Pokud si přejeme mít úhel ve stupních, vydělíme radiány hodnotou (180 / M_PI).

tady by melo byt "vynasobime radiany hodnotou (180/M_PI)"
nebo "vydelime hodnotou (M_PI/180)"

jinak dekuju moc za tenhle tutorial, je fakt super, jsem rad, ze na netu neco takoveho je!

 
Odpovědět
3.10.2018 13:45
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Patrik Valkovič
Šéfredaktor
Avatar
Odpovídá na jan široký
Patrik Valkovič:3.10.2018 18:51

Radiány opraveny.
Funkce abs má přetížení pro všechny základní typy
https://en.cppreference.com/…ic/math/fabs
https://en.cppreference.com/…ric/math/abs

Odpovědět
3.10.2018 18:51
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
Patrik Pastor:25. srpna 17:33

float f = -123.123;
printf("%i\n", signbit(f));

kdyz mam u funkce signbit() argument typu float - zaporny, vraci mi zaporne cislo (velk treba -443456347 --neni nahodne, 5 jsem to exekutnul a stale to same cislo), nemelo by mi to snad vracet kladnou 1? Kdyz bych typ float zmenil na typ double (a vsechno ostatni nechal stejne, tedy double hodnota 123.123) NAJEDNOU mi to uz vraci kladnou jednicku. Proc tedy signbit funguje jen u double kdyz ma napsane v man definici "every floating-point number" coz float je taky. Vysvetleni prosim

 
Odpovědět
25. srpna 17:33
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Patrik Pastor
DarkCoder:25. srpna 18:35

signbit není funkce ale makro a vrací nenulovou integrální hodnotu pokud desetinné číslo předané jako argument začíná znaménkem -. V opačném případě vrací nulu. Je tedy úplně jedno jestli vrátí 1, 10, -10, -100, …), pořád to říká, že desetinné číslo začíná -. Funguje to správně jak u float tak i double typů. Umí to odlišit i následující argumenty 0.0, +0.0 a -0.0.

Funkci makra signbit ulazuje následující program:

#include <stdio.h>
#include <math.h>

int main(void) {

        float fpole[] = { +0.0f, 0.0f, -0.0f, 10.0f, -10.0f };
        double dpole[] = { +0.0, 0.0, -0.0, 100.0, -100.0 };
        int fsize = sizeof(fpole) / sizeof(float);
        int dsize = sizeof(dpole) / sizeof(double);

        // floats
        for (int i = 0; i < fsize; i++) {
                if(signbit(*(fpole + i))) printf("%f - signbit\n", *(fpole + i));
                else printf("%f + signbit\n", *(fpole + i));
        }

        putchar('\n');

        // doubles
        for (int i = 0; i < dsize; i++) {
                if (signbit(*(dpole + i))) printf("%f - signbit\n", *(dpole + i));
                else printf("%f + signbit\n", *(dpole + i));
        }

        return 0;
}
Odpovědět
25. srpna 18:35
"„Učíš-li se proto, aby sis zapamatoval, zapomeneš. Učíš-li se proto, abys porozuměl, zapamatuješ si."
Avatar
Odpovídá na DarkCoder
Patrik Pastor:25. srpna 18:59

co je makro? proc by signbit nemohla byt funkce, kdyz prijima argumenty a vraci nejaky datovy typ - to jsou prece fundamentaly funkce (tyda ma vstupni a vystupni paramter), jak se teda lisi makro.od funkce?

 
Odpovědět
25. srpna 18:59
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Patrik Pastor
DarkCoder:25. srpna 19:18

Makro je v mnoha ohledech podobné funkci. Stejně jako funkce může vykonávat nějakou činnost, může mít parametry. Je tu však určitá odlišnost. Výhoda makra s parametry oproti funkcím je ta, že kód generovaný makrem se začleňuje přímo do kódu programu a nedochází tak ke ztrátě času, který je potřebný pro operace volání a návratu z funkce. Je tedy rychlejší. Makra s parametry se ale používají pro jednoduché operace, neboť při každém volání makra dochází také ke generování duplicitního kódu, takže výsledný program tak může být delší než za použití funkce.

Makro signbit by mohlo být vytvořeno jako funkce, ale proč. Je to operace jednoduchá, tak proč ji tvořit jako funkci. Na podobné operace jako je signbit jsou makra určena.

Odpovědět
25. srpna 19:18
"„Učíš-li se proto, aby sis zapamatoval, zapomeneš. Učíš-li se proto, abys porozuměl, zapamatuješ si."
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 10.