cpp píše:Dobrý programátor musí umět používat knihovny.
A někteří je dokonce umí napsat
Tak ještě pořád čekám na to vzorové řešení, doufám že se ho dočkám. Tohle je moje řešení, odkazující na onu xenofobní nulu z předchozí diskuze.
Tedy pro začátek druhá odmocnina, začneme od toho nejjednoduššího co se dá spočítat:
Kód: Vybrat vše
/* Funkce my_sqrt počítá druhou odmocninu z reálného čísla zadaného parametrem x. Funkce vrací reálné číslo. */
float my_sqrt(float x, int *err)
{
return *err=(x=(e2f(l2f(x)/2)),x==x&&Inf!=x&&-Inf!=x)?0:1,x;
}
Libovolná celočíselná mocnina, tady to už trochu komplikuje znaménko mocnitele:
Kód: Vybrat vše
/* Funkce my_pow počítá n-tou mocninu (zadanou parametrem n, n je celé číslo) reálného čísla zadaného parametrem x. Funkce vrací reálné číslo. */
float my_pow(float x, const int n, int *err)
{
if (x>=0) x=e2f(l2f(x)*n);
else x=((n%2)?-1:1)*e2f(l2f(-x)*n);
*err=(x==x&&Inf!=x&&-Inf!=x)?0:1;
return x;
}
Libovolná celočíselná odmocnina, bonusový "static" v parametru funkce, což mě i můj překladač dost pobavilo:
Kód: Vybrat vše
/* Funkce my_nth_root počítá n-tou odocninu (zadanou parametrem n, n je celé číslo) reálného čísla zadaného parametrem x. Funkce vrací reálné číslo. */
float my_nth_root(float x, /*static*/ int n, int *err)
{
if (x>=0) x=e2f(l2f(x)/n);
else x=-e2f(l2f(-x)/n);
*err=(x==x&&Inf!=x&&-Inf!=x)?0:1;
return x;
}
Pár poznámek:
- Typ float má tak mizernou přesnost, že se hodí leda k počítání domácích úkolů na základní škole. A někdy dvaatřicet bitů ani na to nestačí! Takže maximální přípustnou chybu jsem zcela ignoroval. Pro srovnání, osmibitový ZX BASIC na Sinclairu počítal se čtyřicetibitovými čísly, šestnáctibitový Turbo Pascal od Borlandu s osmačtyřicetibitovými, což přesností oboje postačuje i na vědecké výpočty, s výjimkou astronomických. Jazyk Cobol, určený pro finanční výpočty, dokonce kvůli přesnosti počítá desítkově. Zato float se musí při výpisu vždy zaokrouhlit, aby vyšlo aspoň něco.
- Je nesmysl programovat matematickou knihovnu nebo funkce v ní obsažené, protože to už provedli jiní, trvalo jim to desítky let a udělali to mnohem lépe než bych dokázal já. Také nebudu tvořit další bc nebo dc, ze stejného důvodu jako v předchozí větě. Proto jsem zvolil (pro mne a podle mne) nejjednodušší metodu. Není ideální, není nejrychlejší, není moc přesná, je prostě pro daný účel "good enough" a také funkční.
- Je nesmysl programovat matematickou knihovnu, ale naopak je velmi vhodné vědět jak funguje, a umět něco aspoň vzdáleně podobného udělat sám. Jinak to dopadne tak jak ukazují "moderní trendy", že kódovací opice googlí metodu na porovnání dvou čísel, a tu pak vlepí do svých bastlů, vyžadujících čtyři jádra a osm giga RAM na věc, která se dá udělat třemi instrukcemi. A že by chápaly a uměly napsat aspoň bubblesort nepřipadá v úvahu, to je pro ně sprosté slovo.
- Moje řešení nezná chybné vstupní hodnoty, pouze chybné výsledky, takže chybu hlásí jen v případě že vyšlo Nekonečno nebo Nečíslo. A protože nehodlám implementovat kompletní IEEE 754, dokáží ty funkce spočítat i věci, které podle normy spočítat nejdou. Je-li to problém, musí ho ošetřit programátor který je ve svém programu použije, což se u knihoven třetích stran stává dost běžně.
► Zobrazit spoiler
Nebylo to součástí zadání, ale pro zkompilování a použití je potřeba inkludovat "knihovnu" s výpočtem dvojkového logaritmu a exponentu, tady je:
Kód: Vybrat vše
#ifndef _FEYNMAN_
#define _FEYNMAN_ 1
const union {int i;float f;} _nan_={0X7FC00000},_inf_={0X7F800000};
# define NaN (_nan_.f)
# define Inf (_inf_.f)
const float _feynman_[23][2]={{1+1./(1<< 1), 5.849625E-1},
{1+1./(1<< 2), 3.219281E-1},
{1+1./(1<< 3), 1.69925E-1},
{1+1./(1<< 4), 8.746284E-2},
{1+1./(1<< 5), 4.439412E-2},
{1+1./(1<< 6), 2.2367813E-2},
{1+1./(1<< 7), 1.12272554E-2},
{1+1./(1<< 8), 5.6245492E-3},
{1+1./(1<< 9), 2.8150156E-3},
{1+1./(1<<10), 1.4081944E-3},
{1+1./(1<<11), 7.04269E-4},
{1+1./(1<<12), 3.5217748E-4},
{1+1./(1<<13), 1.760995E-4},
{1+1./(1<<14), 8.805243E-5},
{1+1./(1<<15), 4.4026887E-5},
{1+1./(1<<16), 2.201361E-5},
{1+1./(1<<17), 1.100685E-5},
{1+1./(1<<18), 5.50343433E-6},
{1+1./(1<<19), 2.7517198E-6},
{1+1./(1<<20), 1.3758605E-6},
{1+1./(1<<21), 6.8793044E-7},
{1+1./(1<<22), 3.4396526E-7},
{1+1./(1<<23), 1.7198264E-7}};
/* log2f */
float l2f(float f)
{
int i;
float v;
if (f==f && Inf!=f)
if (f>0)
if (f>=1)
{
for (v=0;f>=2;++v) f/=2.;
for (i=0;i<23;++i)
if (f>=_feynman_[i][0])
{
f/=_feynman_[i][0];
v+=_feynman_[i][1];
}
return v;
}
else return -l2f(1/f);
else if (f<0) return NaN;
else return -Inf;
else return f;
}
/* exp2f */
float e2f(float f)
{
int i;
float v;
if (f==f && Inf!=f)
if (-Inf!=f)
if (f>=0)
{
for (v=1;f>=1;v*=2) f-=1;
for (i=0;i<23;++i)
if (f>=_feynman_[i][1])
{
f-=_feynman_[i][1];
v*=_feynman_[i][0];
}
return v;
}
else return 1/e2f(-f);
else return 0;
else return f;
}
#endif /* feynman.c */