X36UNX 16 Numerické výpočty v sh příkazy expr, bc, dc Zdeněk Sojka sojkaz1@fel.cvut.cz
dc desk calculator - zadávání příkazů postfixově - data se ukládají do stacku - příkazy obyčejně pracují s jedním nebo dvěma operandy na vrcholu stacku - je možné napsat na jeden řádek více příkazů, nemusí ani být odděleny mezerami - bylo by to nepřehledné - není možné vkládat komentáře
Základní příkazy f vypíše celý stack p vypíše prvek z vrcholu zásobníku c vyprázdní hlavní stack d push vrcholu zásobníku zdvojení prvku q konec programu
Vkládání čísel - je možné vkládat v jakékoli soustavě se základem 2 16 - povolena desetinná tečka - záporná čísla se označují podtržítkem, například _1.65 - binární minus by způsobilo odečtení dvou operandů na vrcholu zásobníku - nezávisle na volbě soustavy je možné používat čísla 0-9 a písmena A-E FF vloží při bázi 10 číslo 165
Základní matematické operace - namísto dvou nejvyšších operandů vloží jejich funkci Sčítání + 5 3 f # zadání dvou čísel a vypsání stacku 3 5 + f # součet a vypsat 8 Odčítání - 5 3 - f 2 Násobení * 5 3 * f 15 Dělení / 5 3 / f 1 c # smazání stacku 3 k 5 3 / f # k nastavení přesnosti na 3 řády 1.666
Modulo % 5 3 % f 2 Celočíselná mocnina ^ 5 3 ^ f 125 c 5 3.5 ^ f exp not an integer Odmocnina v 256 v f 16 _16 v # minus 16 sqrt of neg number
Práce s dočasnými registry - fungují jako stack - 256 registrů dostupné pod znaky ASCII - nejlépe se přistupuje k těm, které mají označení 0-9, a-z, A-Z, mezera, enter Push vrcholu zásobníku do registru Sx Pop vrcholu registru do zásobníku Lx - je-li registr prázdný, operace se neprovede Přepsání vrcholu registru sx Kopie vrcholu registru na zásobník lx - je-li registr prázdný, vrací 0
Příklad Sx, Lx, sx, lx 3 Sa 4 Sa 5 sa f empty stack # hodnota 3 se přesunula do registru a, 4 také, ale pak byla přepsána hodnotou 5 la La La la f 0 # hodnota z posledního la, kdy byl registr prázdný 3 # La, hodnota uložená prvním Sa 5 # La, tedy pop vrcholu registru # číslo 4 bylo přepsáno pomosí sa 5 # la, okopírování vrcholu registru La L? # chyba, registr je prázdný f 0 # stack se nezměnil 3 5 5
Práce s číselnými soustavami Nastavení číselné soustavy pro vstup i 16 i f empty stack # stack je prázdný, hodnota se popnula 11 i # 17 v desítkové soustavě! input base is too large F0 f 240 f0 f # zadáno malým písmem je to příkaz ~ f 0 f 240 # první f 0 # druhé f, uložená 0 240 Push číselné soustavy vstupu I I f 10 # defaultně desítková soustava
Nastavení soustavy pro výpis o 5 o f empty stack 123456789 # velké číslo na vrchol stacku 16 o f # vypsat v šestnáctkové soustavě 75BCD6F 30 o f # při větším základu se už nepoužívaj písmena 05 02 12 14 09 09 100 o f output base is too large 05 02 12 14 09 09 # soustava se nezměnila 99 o f # nejvyšší soustava je 99ková 01 28 23 35 18 Push výstupní soustavy O 5 o O f # nastavení pětkové soustavy 10 # základ vypadá vždy jako 10 10 o f # desítková soustava pro výpis 5 # v desítkové je vidět, že tam byla 5
Různá přesnost Nastavení přesnosti výpočtu k 50 k 2 v f # odmocnina ze 2 na 50 míst 1.41421356237309504880168872420969807856967187537694 0 k 2.000 v f # nulová přesnost, ale zadáno číslo se třemi místy 1.414 # přesnost se zachovala Zjištění přesnosti výpočtu K K f 0 # defaultně se počítá jen s celými čísly
Řetězce Vložení řetězce do stacku [abcdef] f abcdef [ ] Výpis řetězce z vrcholu stacku a jeho smazání [abcdef] P f abcdefempty stack P Spuštění příkazu na vrcholu zásobníku x [2.000 v f] x # uložení řetězce a jeho spuštění 1.414 Porovnání a podmíněné spuštění příkazu z vrcholu registru >r <r =r [2.000 v f c] sa f # příkaz do registru a empty stack 1 2 >a # push 1,2,porovnání,vykonání příkazu a!není mezera! 1.414 1 2 <a f empty stack
Příklady Výpis faktoriálu 1...5 (man) [la 1 + # načtení registru a, zvýšení o 1, d sa # uložení kopie zpět do a * p # vynásobení hodnoty na stacku a výpis la 5 >y] # načtení a, porovnání s 5 pokud je menší, znovu se spustí program v registru y sy # uložení do registru y 0 sa 1 # 0 -> a, 1 na stack ly x # načtení y a spuštění Celý kód (pro zkopírování do dc): [la1+dsa*pla5>y]sy 0sa1 lyx 1 2 6 24 120
Geometrická posloupnost 1.25 sa # a0 1.8 sq # kvocient 5 sn # počet čisel 5 k # přesnost práce la # načtení na stack [p # výpis lq * # vynásobení kvocientem ln 1 - # načtní n, snížení o 1 d sn # uložení kopie zpět do n, druhé číslo zůstává na stacku 0 <f # push 0, porovnání a pop dvou nejvyšší čísel pokud nerovnost platí, spustí se program uložený v f ] sf # uložit podprogram do f lf x c # načíst f a spustit, pak smazat stack Celý kód (pro zkopírování do dc) 1.25 sa 1.8 sq 5 sn 5 k la [p lq * ln 1 - d sn 0 <f] sf lf x c 1.25 2.250 4.0500 7.29000 13.12200
Výpočet funkce sinus sin x = x1 1! x3 3! x5 5! x7 7! = n=0 1 n x 2 n 1 2 n 1! 1.000 sx 6 sy # stupeň Taylorova polynomu 20 k # přesnost výstupu lx d sa # iniciální hodnoty, dočasná hodnota sin na stacku 1 d sc sb 0 se [la lx * lx * _1 * d sa lb lc 1 + d sc * lc 1 + d sc * d sb / + p le 1 + d se ly >f] sf lf x # spuštění
Detailnější popis funkce [la lx * lx * _1 * d sa # a = čitatel zlomku = a * x * x * (-1) lb lc 1 + d sc * lc 1 + d sc * d sb # b = jmenovatel zlomku, # faktoriál = b * (c + 1) * (c + 2) # c = aktuální argument faktoriálu / # vydělení čitatele a jmenovatele + # přičtení k hodnotě na vrcholu zásobníku p # vypsání aktuální iterační hodnoty le 1 + d se # zvýšení iterace ly >f] # porovnání s y a případné spuštění f sf # uložení celého řetězce do f Příklad pro okopírování do dc 0.123 sx 6 sy 20 k lx d sa 1 d sc sb 0 se [la lx * lx * _1 * d sa lb lc 1 + d sc * lc 1 + d sc * d sb / + p le 1 + d se ly >f] sf lf x c.12268985550000000000.12269009010880702500.12269009002429758116.12269009002431533870.12269009002431533626.12269009002431533626
bc Program rozšiřuje možnosti dc, který je používán jako backend. Parametry bc [-l] [-c] [soubor] -c zobrazování příkazů dc namísto jejich spouštění -l nahraje knihovní funkce (sin s(x), cos c(x), exp e(x), ln l(x), atn a(x) ) a nastaví rozšířenou přesnost pokud je i přepínač -c, zobrazí se nahrávání těchto programů v jazyce dc. Jedná se tedy o nahrání funkcí do některých registrů soubor načítá vstup ze souboru, po jeho dokončení čte ze stdin
Syntaxe příkazů - obdoba jazyka C, zjednodušení - pouze jednoznakové proměnné, malá abeceda a z if ( x==6 ) { y=7 z=8 # pokud x==6, nastaví se proměnné } while (x<7) x++ while (x<7) x=x+1 # zvýší a vypíše x # nevypisuje x for (i = 0; i <= 3; i+=0.5) s(i) #vypíše hodnoty sinus 0 3 define f(x) { auto a a=x y=x return(x) } # funkce, předávání hodnotou # deklarace proměnné # nastaví se pouze lokální proměnná # globální proměnná # navrácení hodnoty
Nastavení proměnné z příkazové řádky > bash > set -x > a=$(echo "sqrt(2)" bc -l) ++ echo 'sqrt(2)' ++ bc -l + a=1.41421356237309504880 > a=$(echo $a*"sqrt(2)" bc -l) ++ echo '1.41421356237309504880*sqrt(2)' ++ bc -l + a=1.99999999999999999999 > sh > set -x > a=`echo "sqrt(2)" bc -l` + + echo sqrt(2) bc -l a=1.41421356237309504880 > a=`echo $a*"sqrt(2)" bc -l` + bc + echo 1.41421356237309504880*sqrt(2) -l a=1.99999999999999999999
expr - vyhodnocování jednoduchých výrazů - problémy s náhradou na příkazové řádce - operandy je nutné oddělovat mezerou Manuál man -s 2 expr
Příklady sh > sh > set -x > a=7 > a=`expr $a + 1` # obrácené apostrofy! + expr 7 + 1 a=8 > a="expr $a + 1" a=expr 8 + 1 # a nastaven na řetězec > a='expr $a + 1' a=expr 8 + 1 # také řetězec
Příklady bash > bash > set -x > a=1 > a=$(expr $a + 1) # toto nejde v sh ++ expr 1 + 1 + a=2 > expr 2 \* 2 # pozor na nahrazení! + expr 2 '*' 2 4 > expr \( 1 \ 2 \) + expr '(' 1 ' ' 2 ')' 1 # true > expr \( 1 \ 1 \) \& 0 # už to začíná být nepřehledné... + expr '(' 1 ' ' 1 ')' '&' 0 0 # false
Úloha 16 Napište příkaz pro program dc, který vytvoří program na zobrazení n (n>=2) prvků Fibonacciho posloupnosti. Parametr pak bude v registru n, může se měnit. Funkce musí pracovat rekurzivně, ne použitím vzorce. Program bude uložen v registru f. Je očekáváno chování: 5 sn lf x 1 1 2 3 5 Návod (doporučení) - první dvě čísla vypište bez počítání - je dobré mít dva registry, v jednom vždy větší číslo, ve druhém menší - můžete donutit funkci modifikovat sama sebe - nejdříve nastaví registry a b, vypíše 1 1 a pak přepíše registr f, který spustí - příklad [1 d p p sa sb [ kód funkce ln 1 - d sn 2 <f ] sf lf x] sf (zde kód funkce skoro už jen sčítá a vypisuje čísla)