Matematika v PHP

Stejně jako v ostatních jazycích, tak i v PHP jsou čísla reprezentována ve dvojkové soustavě (soustava jedniček a nul), při některých matematických operacích proto může vycházet lehce odlišný výsledek, než bychom čekali. Tato stránka obsahuje přehled chování výpočtů v různých kontextech. Pro pochopení je potřeba aspoň základní znalost číslicové techniky a číselných soustav.

Reprezentace čísel v paměti

Způsob reprezentace čísel charakterizuje datový typ, například integer je převeden přímo ze zdrojového kódu z desítkové soustavy na dvojkovou (binární). O převod čísel se nemusíme vůbec starat, vše probíhá automaticky.

Důsledek tohoto převodu je, že maximální hodnota integeru (celé číslo) je určena podle počtu dostupných bitů v paměti. Na 32-bitovém PHP je rozsah od -2,147,483,648 do 2,147,483,647 (~ ± 2 miliardy), 64-bitové PHP má rozsah od -9,223,372,036,854,775,808 do 9,223,372,036,854,775,807 (~ ± 9 quintillionů). Maximální hodnotu můžeme vždy získat zavoláním konstanty PHP_INT_MAX.

Desetinná čísla se ukládají do datového typu float, který má omezenou přesnost, proto se interně ukládá jako dvojice čísel, které podléhají matematické operaci mantisa * (2 ^ exponent). Praktický důsledek je ten, že jsme schopni na malé množství bitů uložit i relativně velké číslo (v řádu miliard), akorát ne příliš přesně.

Důsledek použití datového typu float je, že výsledkem výpočtu 1 - 0.9 není přesně 0.1.

Příklad z webového fóra:

$x = 0.3;
$y = 0.4;
echo 0.7 - $x - $y; // vypíše -5.5511151231258E-17

Pokud ovšem čísla lehce upravíme:

$x = 6.5;
$y = 7.5;
echo 14.0 - $x - $y; // vypíše 0

Obecně o této problematice pojednává samostatný článek na webu VTM.e15.cz: Proč počítačům dělají problémy desetinná čísla, případně obecně o plovoucí řádové čárce na Wikipedii.

Obecně je dobré po výpočtu výsledek zaokrouhlit, nebo se desetinným číslům úplně vyhnout. Například cenu nebudeme ukládat v korunách (jako 14.90), ale v haléřích (jako 1490) a poté s ní pracovat přesně. O zaokrouhlování pojednává další kapitola článku.

Matematické operace

$a = 5;
$b = 3;

echo $a + $b;

Pro operace lze použít běžné matematické zvyklosti, při kterých se respektuje přednost operátorů podle pravidel matematiky (násobení má přednost před sčítáním a podobně). Hodnoty lze zapsat přímo, nebo je načítat přes proměnné.

Možná to není na první pohled zřejmé, ale u matematického příkladu se nezapisuje znak rovnítka (=). Z toho tedy vyplývá, že lze řešit jen jednoduché binární operace, které vždy pracují jen s dvěma prvky (s rovnicemi nepočítejte, na to se musí použít specializovaná knihovna).

Přehled základních operátorů

Ve sloupci výsledek uvádím, co se vypíše za předpokladu, že:

$a = 5;
$b = 3;
$c = 10;
Operace Značka Značka slovně Příklad zápisu Výsledek
Sčítání + Plus echo $a + $b; 8
Odčítání Mínus echo $a – $b; 2
Násobení * Hvězdička echo $a * $b; 15
dělení / Lomítko echo $a / $b; 1.66666666667
Závorky ( ) Závorky echo $a + ($b * $c); 35
Spojování řetězců . Tečka echo $a . $b . $c; 5310
Zbytek po dělení % Procento echo $a % $b; 2
Přičtení jedničky ++ dva plusy echo $a++; 6
Odečtení jedničky -- dva mínusy echo $a--; 4
Mocnina ** dvě hvězdičky echo $a ** $b; 125

Operátor mocniny (dvojitá hvězdička) je dostupný až od PHP 7.1. V jiných verzí PHP je nutné použít univerzální funkci pow($a, $b).

Přehled základních matematických funkcí

Pokud řešíte nějaký běžný výpočetní úkol, tak s největší pravděpodobností již existuje funkce přímo v PHP, zde je jejich komentovaný přehled.

Zaokrouhlování

Běžné zaokrouhlení se provádí funkcí round(), má 3 parametry:

  • Zaokrouhlované číslo
  • Nepovinný: Přesnost (na kolik desetinných míst)
  • Nepovinný: Půlící hodnota (od jaké hodnoty se má zaokrouhlovat nahoru)
round(5);               // 5
round(5.1);             // 5
round(5.4);             // 5
round(5.5);             // 6
round(5.8);             // 6
round(5483.47621, 2);   // 5483.47
round(5483.47621, -2);  // 5500

Dále existuje funkce floor() pro zaokrouhlení směrem dolů a funkce ceil() pro směr nahoru.

PHP pracuje s různými datovými typy, například u floatu se může snadno stát, že výsledkem funkce round() nebude celé číslo, protože nelze ve dvojkové soustavě snadno zapsat. Řešením je použití funkce number_format(), o které pojednávám níže.

Goniometrické funkce (týkají se jednotkové kružnice)

Používají se na hrozně moc věcí. Např. vykreslování kružnic, elips, posunů…

sin();
cos();
tan();

Cyklometrické funkce (týkají se jednotkové kružnice)

Výpočet úhlu z x a y. Převody kartézských a polárních souřadnic.

asin();
acos();
atan();

Hyperbolické funkce (týkají se jednotkové hyperboly)

Dalo by se říct takové jednoduché přirovnání:

  • Elipsa, Kružnice, Kružnice s poloměrem 1
  • Hyperbola, Rovnoosá hyperbola, Jednotková hyperbola

Používají se např. při generování terénu, fyzikálních simulacích.

sinh();
cosh(); // řetězovka
tanh();

Hyperbolometrické funkce (týkají se jednotkové hyperboly)

asinh();
acosh();
atanh();

Několik příkladů volání goniometických funkcí:

echo sin(30);               // -0.98803162409286
echo sin(deg2rad(30));      // 0.5
echo cos(deg2rad(123));     // -0.54463903501503
echo 1/tan(deg2rad(45));    // 1 (onen cotangens)
echo sin(deg2rad(500));     // 0.64278760968654
echo atan2(deg2rad(50), deg2rad(23)); // 1.1396575860761

Operace s velkými čísly a přesnost výpočtů

Velká čísla a desetinná čísla se v PHP ukládají jako float, navíc se zaokrouhlují přibližně na 15 desetinných míst, což může být někdy nepříjemné. Proto vznikla knihovna BCMath Arbitrary Precision Mathematics.

Čísla se v této knihovně reprezentují jako string, proto nejsou limitovány nepřesností floatu, nicméně prováděné operace jsou o několik řádů pomalejší (ale stále jde o rychlejší způsob, než kdybychom si takovou funkci naprogramovali sa­mi).

Pokud chceme získat například odmocninu ze 2 na 3 desetinná místa, tak stačí jen zavolat:

echo bcsqrt('2', 3); // 1.414
Sponzorované odkazy
Pomohl Vám tento článek?

Přehled důležitých matematických funkcí

abs(x) ~ absolutní hodnota

exp(x) ~ exponencioální funkce

expm1(x) ~ je definován jako exp(x) - 1

fmod(x, y) ~ vrací desetinný zbytek po celočíselném dělení x / y

hypot(x, y) ~ výpočet přepony u pravoúhlého trojúhelníku (pythagorova věta)

log(x, y) ~ logaritmus x o základu y. Pokud nezadáte základ, je defaultní hodnota konstanta M_E2.718281828459)

log10(x) ~ logaritmus o základu 10

log1p(x) ~ logaritmus (1 + x)

min(x) a max(x) ~ najde minimální nebo maximální hodnotu z pole čísel.

rand(x, y) a mt_rand(x, y) ~ funkce generování čísel (mt_rand má vylepšený algoritmus)

pow(x, y) ~ mocnina x na y

sqrt(x) ~ vrací druhou odmocninu x, druhým parametrem lze nastavit, jakou odmocninu vrátit

Přehled matematických konstant

M_PI ~ 3.14159265358­979323846 → π

M_PI2 ~ 1.57079632679­489661923 → π/2

M_PI4 ~ 0.78539816339­744830962 → π/4

M1_PI ~ 0.31830988618­379067154 → 1/π

M2_PI ~ 0.63661977236­758134308 → 2/π

M_SQRTPI ~ 1.77245385090­551602729 → odmocnina z π

M2_SQRTPI ~ 1.12837916709­551257390 → 2 / odmocnina z π

M_LNPI ~ 1.14472988584­940017414 → log_e(π)

M_SQRT2 ~ 1.41421356237­309504880 → odmocnina z 2

M_SQRT3 ~ 1.73205080756­887729352 → odmocnina z 3

M_SQRT1_2 ~ 0.70710678118­654752440 → 1 / odmocnina z 2

M_EULER ~ 0.57721566490­153286061 → Eulerova konstanta

Proč funkce round() někdy zaokrouhluje nepřesně?

Může za to způsob reprezentace čísel v paměti počítače. Pokud chcete dosáhnout přesného zaokrouhlení, použijte funkci number_format(), která vrátí zaokrouhlené číslo jako string, proto nedojde k poškození přesnosti.

Jak vypočítat průměr dvou čísel?

Na toto v PHP bohužel neexistuje žádná funkce, ale můžeme si ji naprogramovat sami.

echo average(5, 7); // vrátí 6

function average($x, $y) {
    return ($x + $y) / 2;
}

Jak vypočítat průměr čísel, která mám v poli?

Opět na to neexistuje přímo funkce, ale můžeme si ji snadno vytvořit.

echo averageByArray([1, 2, 3, 4, 5]);

function averageByArray($array) {
    $sum = array_sum($array);
    $count = count($array);

    return $sum / $count;
}

Poděkování

Přehled goniometrických funkcí poskytl Pavol Hejný.