Целобројни типови¶
У приручнику Увод у програмирање у програмском језику C# погледајте поглавље 2.3 (стране 22-24).
Целобројни типови и њихови опсези¶
Као и за реалне бројеве, у језику C# постоји више начина да се цели бројеви представе у рачунару, а тиме и више целобројних типова. Они се разликују по томе
да ли могу да представе само бројеве без знака (нулу и позитивне) или и негативне бројеве. Ако целобројни тип може да представи и негативне бројеве, за такав тип кажемо да је означен, иначе је неозначен.
колико места заузимају у меморији
колико велике бројеве могу да представе
Зависно од платформе, типови се могу разликовати и по томе колико брзо могу да се изврше рачунске операције над подацима тих типова.
У проблемима које ћемо решавати у овом курсу, биће нам довољан тип int. Користећи овај тип, могу се исправно обавити сва рачунања са целим бројевима у опсегу између минус две милијарде и две милијарде.
Остали целобројни типови су наведени и описани информативно, јер то помаже општем разумевању ограничења типова података. Знања о другим целобројним типовима потребна су пре свека ученицима који желе да решавају такмичарске задатке из програмирања.
Детаљније о целобројним типовима:
Целобројни тип података може да заузима 8, 16, 32, или 64 бита (то јест 1, 2, 4 или 8 бајтова). Простор од \(N\) бита може да се попуни на \(2^N\) различитих начина, па тип који заузима \(N\) бита може да представља укупно \(2^N\) различитих вредности.
За неозначени целобројни тип, тих \(2^N\) вредности су од \(0\) до \(2^N - 1\).
За означени целобројни тип опсег вредности је од \(-2^{N-1}\) до \(2^{N-1}-1\).
Преглед свих целобројних типова у језику C# и њихових опсега је дат у следећој табели:
¶ Назив типа
меморија
означен
најмања вредност
највећа вредност
byte
8 бита
не
0
\(2^{8} - 1 = 255\)
ushort
16 бита
не
0
\(2^{16} - 1 = 65~535\)
uint
32 бита
не
0
\(2^{32} - 1 = 4~294~967~295\)
ulong
64 бита
не
0
\(2^{64} - 1 = 18~446~744~073~709~551~615\)
sbyte
8 бита
да
\(-2^{7} = -128\)
\(2^{7} - 1 = 127\)
short
16 бита
да
\(-2^{15} = -32~768\)
\(2^{15} - 1 = 32~767\)
int
32 бита
да
\(-2^{31} = -2~147~483~648\)
\(2^{31} - 1 = 2~147~483~647\)
long
64 бита
да
\(-2^{63} = -9~223~372~036~854~775~808\)
\(2^{63} - 1 = 9~223~372~036~854~775~807\)
Приближно одређивање опсега напамет:
У пракси су нам врло ретко потребне тачне вредности граница опсега појединих целобројних типова, али је корисно имати представу о реду величине тих граница. Служећи се познатом проценом \(2^{10} = 1024 \approx 1000 = {10}^{3}\) имамо:
\(2^{31} = 2 \cdot 2^{30} \approx 2 \cdot {10}^{9}\), па 32-битни означени бројеви припадају опсегу од приближно минус две милијарде до плус две милијарде.
\(2^{32} = 4 \cdot 2^{30} \approx 4 \cdot {10}^{9}\), па 32-битни неозначени бројеви припадају опсегу од нуле до приближно четири милијарде.
\(2^{63} = 8 \cdot 2^{60} \approx 8 \cdot {10}^{18}\), па су 64-битни означени бројеви у опсегу од приближно \(-8 \cdot {10}^{18}\) до \(8 \cdot {10}^{18}\).
\(2^{64} = 16 \cdot 2^{60} \approx 16 \cdot {10}^{18}\), па су 64-битни неозначени бројеви из опсега од 0 до приближно \(16 \cdot {10}^{18}\).
Ове процене су често сасвим довољно прецизне, а предност им је што се не морају памтити, већ се увек по потреби могу брзо израчунати.
Као и код реалних бројева, целобројни тип који ћемо користити можемо да изаберемо у складу са потребама, зависно од проблема који решавамо. Када у решавању проблема желимо да употребимо велики број целобројних величина које нису велике по вредности, бирамо тип који заузима мање места и тиме штедимо меморију (а у принципу можемо да обезбедимо и брже извршавање). Са друге стране, ако баратамо вредностима реда величине стотина милијарди (а рачунања нема много тако да заузеће меморије и брзина извршавања нису критични), користићемо тип long или ulong.
Да бисмо потпуније разумели оно што је до сада речено, погледајмо следећи пример:
Покретањем овог програма добијамо резултат:
65536 0Наредбом у осмој линији n добија вредност \(256 \cdot 256 = 65~536\) и та вредност је исписана. Међутим, очигледно је да резултат другог множења (девета линија програма) није исправан, јер је \(65~536 \cdot 65~536 = 4~294~967~296\) а не \(0\). Резултат \(4~294~967~296\) нисмо добили зато што он не може да се смести у променљиву типа int (погледајте табелу горе). Оно што се овде догодило назива зе прекорачење опсега, или кратко - прекорачење (енгл. overflow). Ово је слично као када бројчаник попут овог са слике стигне до 9999 и одброји још 1, па уместо 10000 покаже 0000 јер има само четири цифре.
Овај проблем се може избећи тако што уместо типа int користимо тип long.
Сада извршвањем програма добијамо исправан резултат.
65536 4294967296Наравно, тип long представља само „већи бројчаник”, тако да бисмо при појави још већих бројева опет наишли на исти проблем.
65536 4294967296 0Ако је природа проблема који решавамо програмом таква да основни, уграђени типови нису довољни, решење се може наћи у системској библиотеци. У језику C# постоји једноставно и елегантно решење, које омогућава употребу далеко, далеко већих бројева. Заинтересовани за детаље могу да потраже информације о класи BigInteger која је део именског простора System.Numerics. Рачунање са овако представљеним бројевима је спорије (и захтева више меморије), па га треба користити само ако је то заиста неопходно.
Операције над целобројним типовима¶
Поред 4 уобичајене операције (сабирање, одузимање, множење и дељење) у целобројној аритметици се често користи и рачунање остатка при дељењу. Ова операција се означава симболом %
, па је на пример вредност израза (17 % 5)
једнака 2, јер је 2 остатак при дељењу 17 са 5.
Напомене о приоритету оператора и употреби заграда су исте као и за реалне бројеве. Поменимо и то да је оператор %
истог приоритета као оператори *
и /
, а при томе такође лево асоцијативан, дакле 20 % 12 % 3
је исто што и (20 % 12) % 3
.
При дељењу целобројних типова података треба имати на уму једну специфичност. Наиме, када су и дељеник и делилац целобројног типа, оператор /
означава такозвано целобројно дељење, то јест дељење са занемаривањем остатка. На пример, извршавањем следећег програма
добијамо резултат
3.4
3
Видимо да је при целобројном дељењу остатак једноставно одбачен.
Једна честа грешка¶
Почетници често праве различите грешке у вези са целобројним дељењем. Једна типична грешка је писање наредби попут
double a = 1/16; // неисправно
Могло би се очекивати да ће извршавањем ове наредбе променљива a да добије вредност \({1 \over {16}} = 0.0625\), али то се неће догодити. Редослед дешавања је такав да се прво израчуна израз на десној страни знака једнакости, а пошто су оба операнда целобројног типа, резултат је 0 (остатак се одбацује). Затим се тај резултат додељује реалној променљивој и том приликом се мења начин записивања броја 0 из целобројног у реални.
Да би реална променљива a добила вредност \(1 \over {16}\), потребно је да се дељење обави као дељење реалних бројева, а то ће бити случај ако је бар један од операнада (дељеник, делилац) реалног типа.
Зато резултат \(1 \over {16}\) можемо постићи било којом од ових наредби
double a = 1.0 / 16; // исправно
double a = 1 / 16.0; // исправно
double a = 1.0 / 16.0; // исправно
Слично овоме, када хоћемо да променљива a добије вредност \(1 {1 \over {16}}\), наредба
double a = 1 + 1 / 16; // неисправно
наравно, неће довести до жељеног резулатата јер је дељење целобројно. Међутим, до правог резултата не доводи ни наредба
double a = 1.0 + 1 / 16; // неисправно
јер дељење има виши приоритет од сабирања, па се прво израчунава количник. Како су оба операнда дељења целобројна, дељење је и даље целобројно, а до конверзије у реалан број долази тек пред сабирање, када је остатак већ изгубљен. Исправан резултат се може добити претварањем дељеника или делиоца (или оба) у реалан тип, као што је показано у претходном примеру:
double a = 1.0 + 1.0 / 16; // исправно
double a = 1 + 1.0 / 16; // исправно
double a = 1.0 + 1 / 16.0; // исправно
итд.
Први сабирак може да буде реалан (1.0) или целобројан (1), али ће коначан резултат (збир) свакако бити реалан, јер је други сабирак (резулатат реалног дељења) реалан.
У квизу који следи можете да проверите да ли умете правилно да користите целе бројеве и операције над њима на језику C#. Покушајте затим да решите и задатке, у којима ћете вежбати употребу целобројних вредности и израза у програмима.