Садржај
3 Променљиве, подаци, типови
3.5 Текстуални подаци (стрингови, ниске)
4 Гранања
4.7 Гранања - разни задаци
5 Петље
5.1 Врсте петљи
5.2 Наредбе break и continue
6 Статички методи
6.4 Корист од метода
7 Низови
7.2 Низови - вежбање
8 Матрице
9 Кориснички дефинисани типови
10 Фајлови

Конверзије нумеричких типова и заокруживање

У приручнику Увод у програмирање у програмском језику C# погледајте поглавље 2.4 (стране 25-34).


Реч конверзија значи преобраћање, преобликовање, претварање из једног облика у други. Када говоримо о конверзији података у програмирању, мисли се на промену типа датог податка уз задржавање вредности.

Имплицитна конверзија се дешава без посебног, изричитог захтева, прећутно, она се просто подразумева у неким ситуацијама.

Експлицитна конверзија се дешава само на изричит захтев, тако што у програм додамо неки од записа који значи „конвертуј” (постоји више врста таквих записа).

Имплицитна конверзија

Рачунар је прављен тако да може да обавља операције само над бројевима истог типа. У програмима на разним програмским језицима је допуштено писати изразе у којима операнди нису истог типа, на пример 2 + 3.5, али рачунар пре одређивања вредности таквог израза мора да конвертује један од операнада. Опште правило је да се операнд ужег типа конвертује у тип другог операнда, који је шири. У датом примеру то значи да се целобројна вредност 2 конвертује у реални тип да би сабирање могло да се обави.

Овакве конверзије се дешавају аутоматски и зову се имплицитне конверзије (подразумеване конверзије).

Када смо говорили о целобројном дељењу, поменули смо да је за добијање реалног резултата важно да не буду оба операнда целобројна, него да бар један од њих треба да буде реалан, на пример:

double a = 1.0 / 16;

Овде управо долази до имплицитне конверзије целобројне вредности 16 у реалну 16.0 а затим се извршава операција реалног дељења.

Имплицитне конверзије се врше и у другим ситуацијама које се могу разрешити преласком на шири тип. На пример, у наредби

a = Math.Sqrt(2);

математичка функција очекује аргумент реалног типа а добија целобројни аргумент, па пре рачунања вредности функције њен аргумент мора да се конвертује у одговарајући реалан тип.

Имплицитна конверзија се дешава и када променљивој ширег типа додељујемо вредност ужег типа, на пример

double t = 3;

Пре уписивања вредности у променљиву t, потребно је формирати запис броја 3 који одговара типу double, то јест потребно је конвертовати целобројну вредност 3 у тип double.

Експлицитна конверзија

Каст

Има ситуација када нам је потребно да податак ширег типа конвертујемо у неки ужи тип, на пример реалан у целобројни. Таква конверзија се никада не дешава аутоматски. У том случају можемо да инсистирамо да се вредност конвертује тако што то напишемо у програму, овако:

double t = 3.35;
int i = (int)t;

Писањем типа у загради испред вредности, ми тражимо да се вредност конвертује у наведени тип. Овде ће вредност променљиве t бити конвертована у тип int, а затим уписана у променљиву i. Оваква конверзија се назива каст, од енглеског type cast, што дословно значи „ливење типа”, а овај термин слободније можемо превести као „присилно уклапање у тип”, или још слободније као „калупљење” (мада дословно значење више није исто). Каст је један од облика експлицитне конверзије.

Каст може да буде користан и за конверзију из ужег у шири тип. Нека су m и n две целобројне променљиве и нека је потребно њихов реалан количник (са разломљеним делом) сместити у променљиву a типа double. Као што знамо, наредба

double a = m / n;

је синтаксно исправна и може да се изврши, али њеним извршавањем се прво рачуна резултат целобројног дељења а затим долази до имплицитне конверзије резулатата у реалан запис. На овај начин децимални део количника бива изгубљен и резултат није одговарајући.

Жељени резултат можемо да добијемо помоћу експлицитне конверзије (кастовања) на било који од следећих начина

double a = (double)m / n;
double a = m / (double)n;
double a = (double)m / (double)n;

али не и овако

double a = (double)(m / n);

јер је овде дељење и даље целобројно, па до претварања у реални тип долази прекасно.

Парсирање

Још један облик експлицитне конверзије смо видели већ у нашем првом C# програму, а он је изгледао овако:

double a = double.Parse(Console.ReadLine());

Функција Console.ReadLine() враћа текст, чак и када тај текст садржи запис броја. Пошто је нама потребан податак типа double, користимо функцију double.Parse() која ишчитава и рашчлањује текст (сама реч parse значи рашчланити), па ако текст представља запис реалног броја, функција враћа вредност тог реалног броја као податак типа double.

Осим ова два облика експлицитне конверзије (каст и парсирање) постоје и други, као што је употреба класе Convert или функције TryParse, али ти начини конверзије се неће појављивати у овом курсу.

Функције за заокруживање реалних бројева

Ове функције су у приручнику детаљније објашњене кроз примере, а овде ћемо се само подсетити:

У језику C# постоји неколико функција које пресликавају реалне бројеве у целе. Прецизније речено, њихов резултат је реалног типа, али нема разломљени део (децимале). То су функције Math.Truncate, Math.Floor, Math.Ceiling и Math.Round.

  • функција Math.Round() враћа цео број најближи вредности аргумента (резтултат је реалног типа);

  • функција Math.Floor() враћа најближи цео број, мањи или једнак вредности аргумента (резтултат је реалног типа);

  • функција Math.Ceiling() враћа најближи цео број, већи или једнак вредности аргумента (резтултат је реалног типа);

  • Функција Math.Truncate() враћа цео број, који се добија одбацивањем децимала аргумента (резтултат је реалног типа);

Приметимо да све ове функције имају једну заједничку особину: у случају да је аргумент без децимала, резулатат је једнак аргументу.

Мање формалан начин да опишемо ове функције је:

  • функција Math.Round() заокружује ка ближем целом броју;

  • функција Math.Floor() заокружује ка мањем целом броју;

  • функција Math.Ceiling() заокружује ка већем целом броју;

  • Функција Math.Truncate() заокружује ка целом броју ближем нули;

У случају функције Math.Round није сасвим јасно шта је резултат када је њен аргумент једнако удаљен од два најближа цела броја, то јест када се од целог броја разликује тачно за 1/2. Подразумевано понашање је да се примењује такозвано правило парне цифре, то јест да се у том случају заокружује на онај од два најближа цела броја који је паран. Тако имамо

x

-3.5

-2.5

-1.5

-0.5

0.5

1.5

2.5

3.5

Math.Round(x)

-4.0

-2.0

-2.0

0.0

0.0

2.0

2.0

4.0

О губитку тачности

Пошто се реални бројеви углавном не могу тачно представити у рачунару, ми скоро увек рачунамо са приближним вредностима. Разлика између тачне и приближне вредности је најчешће толико мала да је можемо занемарити. Ипак, када реалне бројеве заокружујемо на целе, треба бити опрезан јер те мале разлике могу да доведу до нетачног резултата.

Размотримо следећи пример: нека је потребно одредити колико целих шоља запремине SOLJA литара се може напунити из бокала у коме има BOKAL литара (SOLJA и BOKAL су реални бројеви). Одговор, наравно, даје израз Math.Floor(BOKAL / SOLJA). Испробавањем ове формуле за за шоље од 0.2 литра и бокал у коме има 0.4 литра (као и за многе друге вредности BOKAL и SOLJA) добићемо тачан резултат и све ће бити у реду.

Испробајмо сада формулу Math.Floor(BOKAL / SOLJA) за шоље од 0.1 литра и бокал у коме има 0.3 литра. Извршавањем наредбе

Console.WriteLine(0.3 / 0.1);

исписује се број 3, као што очекујемо. Међутим, наредба

Console.WriteLine(Math.Floor(0.3 / 0.1));

исписује се број 2, што је прилично изненађујуће.

Да бисмо разумели како је дошло до овог неочекиваног резултата, приметимо да се наредбом

Console.WriteLine(3 - 0.3 / 0.1);

исписује

4.44089209850063E-16

што јесте веома мали број (приближно \(4 \cdot {10}^{-16}\)), али је ипак већи од нуле. То значи да приликом рачунања вредности израза 0.3 / 0.1 добијен број већи од 2.9999999999999995, али мањи од 3. Зато се заокруживањем наниже, односно израчунавањем функције Math.Floor(0.3 / 0.1) добија резултат 2, а не 3.

Овакви проблеми се не дешавају често и у пракси представљају специјалан случај, који се може и игнорисати. Уколико желите да овим специфичним ситуацијама посветите додатну пажњу да бисте избегли нетачан резултат при заокруживању због овако малих грешака, довољно је да количник најпре заокружите на неки број децимала мањи од 16, на пример 9. За то служи други аргумент функције Math.Round, којим се задаје на колико децимала желимо да заокружимо резултат (ако се овај аргумент изостави, подразумева се 0). На пример, за \(x = 123.12856\) вредност функције Math.Round(x) је \(123\), док позив Math.Round(x, 2) враћа вредност \(123.13\), што је вредност \(x\) заокружена на две децимале.

Заокруживањем броја 2.9999999999999995 на девет децимала (линија 11 програма) се добија 3. У нашем случају 9 децимала је тачност реда нанолитара, дакле милионитих делова милилитра, што је далеко тачније него што у реалној ситуацији има смисла и портебе, па овим заокруживањем баш ништа нисмо изгубили. Са друге стране, овим заокруживањем смо поништили грешку која је још много мања (преко милион пута мања од нанолитра), али нам је ипак стварала проблеме.

Следи квиз и задаци за проверу знања у вези са заокруживањем.

(Created using Swinx, RunestoneComponents and PetljaDoc)
© 2022 Petlja
A- A+