Number systems; representation of numbers


Positional number systems

In digital computers, data is generally represented as a sequence of binary digits, e.g. in the form of binary numbers. Since binary numbers are difficult to handle, we usually display them in the hexadecimal number system (i.e. in the positional number system with base 16) as hexadecimal numbers.

A number system (or number representation system) defines uniform rules which determine how we represent numbers by a series of digits.

In general, numbers in a number system with base 'b' can be described or represented by the following formula:
egy szám általános alakja a 'b' alapú számrendszerben

Examples:

(1) a real number in the decimal number system (b=10):
    314.610 = 3*102+1*101+4*100 + 6*10−1

(2) an integer in the binary number system (b=2):
    1010112 = 1*25+1*23+1*21+1*20 = 4310

(3) a real number in the binary number system (b=2):
    101011.1012 = 1*25+1*23+1*21+1*20 + 1*2−1+1*2−3 = 43.62510

(4) an integer in the hexadecimal number system (b=16):
    1C416 = 1*162+12*161+4*160 = 45210

(5) a real number in the hexadecimal number system (b=16):
    1C4.416 = 1*162+12*161+4*160 + 4*16−1 = 452.2510



Examples of algorithms

In computer science, or in general, in the ubiquitous and rapidly evolving information and communication technology (IKT), the concept of algorithm is fundamental.

An algorithm is a prescribed set of well-defined instructions or statements for the solution of a given problem (or a group of similar problems), in a finite number of steps executed in a given order (e.g. the performance of a calculation).

The algorithms that are important to us are those that can be described ("encoded") with the help of a computer program. In that case, we correspond to statements or instructions that perform specific actions or operations for each step of the algorithm.

We will provide a number of programs for the formal description of the algorithms described in this chapter. The most important components of these programs are reviewed below.

The examples here are given in the JavaScript programming language. To get to know the language more thoroughly, it is strongly recommended to study the following materials which are ideal for self-study:

JavaScript Tutorial (2022-09-27)
JavaScript Algorithms and Data Structures (2022-10-02)

The simplest programs are built from the following statements:

The three most important control structures that determine the execution order of the statements are as follows:

The above control structures can be excellently illustrated with the help of flowcharts as follows: (cf. Essential Programming | Control Structures | by Diego Lopez Yse | Towards Data Science, 2022-10-09):

the 'if' control structure the 'while' control structure


Below we present specific algorithms related to number systems. We will deal with the following algorithms:

  1. converting a binary number to a decimal number
  2. converting a hexadecimal number to a decimal number
  3. converting a decimal number to a binary number
    • conversion of an integer
    • conversion of a fraction
  4. converting a binary number to a hexadecimal number, and vice versa
  5. converting a decimal number to a hexadecimal number
  6. various representation of numbers
  7. basic operations in the binary number system

In most cases, the description of the algorithms is also presented with short JavaScript programs. An online JavaScript interpreter that can be used to try the example programs can be accessed as follows:

Online JavaScript Interpreter by Peter Jipsen, Chapman University (January 2013).
http://math.chapman.edu/~jipsen/js/ (2020-11-19)

Note: If the link above does not work for some reason, please use the link here.

The flowcharts illustrating the JavaScript programs can be displayed using the online web application below:

Live code editor. Created by Bogdan Lyashenko.
https://bogdan-lyashenko.github.io/js-code-to-svg-flowchart/docs/live-editor/index.html (2022-10-02)


Examples of algorithms:
1. converting a binary number to a decimal number

Example: 11012 = ?10

digit positional value product
1 23=8 8
1 22=4 4
0 21=2 0
1 20=1 1
Total 13

Result:
11012 = 1310

Check:
1310 = 8 + 4 + 1 = 1*23 + 1*22 + 0*21 + 1*20 = 11012 (ok)


The JavaScript program implementing the above algorithm is as follows:

// converting a binary number to a decimal number 

var x="1101"; 

writeln("Binary number: "+x);
writeln();

var d=0;
var posvalue=1;

for(var i=1;i<x.length;i++) {
 posvalue*=2;
 }

for(var i=0;i<x.length;i++) {
 var sz;
 if(x[i]=="1") {
  sz=1;
  }
 else {
  sz=0;
  }
 writeln("   Digit           : "+sz);
 writeln("   Positional value: "+posvalue);
 var r=sz*posvalue;
 writeln("      Partial sum: "+r);
 d+=r;
 posvalue/=2;
 }

writeln();

writeln("Decimal number: "+d);

writeln("__________");



Examples of algorithms:
2. converting a hexadecimal number to a decimal number

Example: 24C16 = ?10

digit positional value product
2 162=256 512
4 161=16 64
C 160=1 12
Total 588

Result:
24C16 = 58810

Check:
58810 = 512 + 64 + 12 = 2*256 + 4*16 + 12* 1 = 2*162 + 4*161 + 12*160 = 24C16 (ok)


The JavaScript program implementing the above algorithm is as follows:

// converting a hexadecimal number to a decimal number 

// var x=["2","4","C"]; 
var x="24C";

write("Hexadecimal number: ");

for(var i=0;i<x.length;i++) {
 write(x[i]);
 }

writeln();
writeln();

var d=0;
var posvalue=1;

for(var i=1;i<x.length;i++) {
 posvalue*=16;
 }

for(var i=0;i<x.length;i++) {
 var sz;
 switch(x[i]) {
  case "a":
  case "A": sz=10; break;
  case "b":
  case "B": sz=11; break;
  case "c":
  case "C": sz=12; break;
  case "d":
  case "D": sz=13; break;
  case "e":
  case "E": sz=14; break;
  case "f":
  case "F": sz=15; break;
  default: 
   sz=parseInt(x[i]);  
  }
 writeln("   Digit           : "+sz);
 writeln("   Positional value: "+posvalue);
 var r=sz*posvalue;
 writeln("      Partial sum: "+r);
 d+=r;
 posvalue/=16;
 }

writeln();

writeln("Decimal number: "+d);

writeln("__________");



Examples of algorithms:
3. converting a decimal number to a binary number

1. conversion of an integer

Example: 31410 = ?2

division quotient remainder
314
314:2= 157 0
157:2= 78 1
78:2= 39 0
39:2= 19 1
19:2= 9 1
9:2= 4 1
4:2= 2 0
2:2= 1 0
1:2= 0 1

Result:
31410 = 1|0011|10102

Note: since a binary number usually consists of quite a lot of digits (i.e. it often seems to be "long"), it is useful to divide the digits into groups of four from the right (using a separator, e.g. the | character).

Check:
1|0011|10102 = 1*28 + 1*25 + 1*24 + 1*23 + 1*21 = 256 + 32 + 16 + 8 + 2 = 31410 (ok)


The JavaScript program implementing the above algorithm is as follows:

// converting a decimal number to a binary number 

var x=314; 

writeln("Decimal number: "+x);
writeln();

var b="";
var q=1;

while(q>0) {
 q=Math.trunc(x/2); // integer division!!
 write("   Quotient: "+q); 
 if(x%2==1) {
  b="1"+b;
  writeln(", remainder: "+1);
  }
 else {
  b="0"+b;
  writeln(", remainder: "+0);  
  }
 x=q;
 }

writeln();
writeln("Binary number: "+b);

writeln("__________");


Kettő hatványai

An alternative way to convert a decimal integer to a binary number is as follows:

  1. find the highest power of two that is less than or equal to the number to be converted; that will be the highest positional value of the binary number
    • the digit corresponding to the found positional value will be '1';
  2. subtract the found positional value from the decimal number to be converted;
  3. considering the above result of the subtraction as a decimal number to be converted, we repeat steps 1 and 2 until the result of the subtraction is zero;
  4. in the binary notation of the decimal number to be converted, the digits to which we have not assigned the value '1' will have the value '0'.

Példa: 31410 = ?2

power of two decimal number to be converted
result of the subtraction
binary digit
314
28=256 314−256=58 1
27=128 58<128 0
26=64 58<64 0
25=32 58−32=26 1
24=16 26−16=10 1
23=8 10−8=2 1
22=4 2<4 0
21=2 2−2=0 1
20=1 0<1 0

Result:
31410 = 1|0011|10102


The JavaScript program implementing the above algorithm is as follows:

// converting a decimal number to a binary number by subtractions

var x=314; 

writeln("Decimal number: "+x);
writeln();

var b="";
var posvalue=1;

while(2*posvalue<=x) {
 posvalue*=2;
 }

while(posvalue>=1) {
 if(posvalue<=x) {
  b+="1";
  writeln("   Digit:            "+1);
  writeln("   Positional value: "+posvalue);
  x-=posvalue;
  }
 else {
  b+="0";
  writeln("   Digit:            "+0);
  writeln("   Positional value: "+posvalue);
  }
 posvalue/=2;
 }

writeln();
writeln("Binary number: "+b);

writeln("__________");


2. conversion of a fraction

For the sake of simplicity, let's assume that the decimal fraction to be converted is positive and less than 1.

Example: 0.687510 = ?2

  1. multiply the decimal number to be converted (e.g. 0.6875) by 2 until
    • the fractional part is 0, or
    • a given sequence of binary digits shows endless repetition.
  2. write the whole part of the product (which can be 0 or 1), and then subtract it from the product
  3. if the algorithm continues, the result of the above subtraction (i.e. the fractional part of the above product) is multiplied by 2 again
  4. repeat from step 2
  5. the binary notation of the decimal fraction to be converted is obtained by writing down the written whole parts of the products in the original order after the starting 0. of the number (e.g. 0.1011)
product whole part fractional part
0.6875
0.6875*2= 1.375 1 0.375
0.375*2= 0.75 0 0.75
0.75*2= 1.5 1 0.5
0.5*2= 1 1 0

Result:
0.687510 = 0.10112

Notes:
(1) in the case of more than four binary digits, it is also worth dividing the sequence of digits into groups of four (from the left, just after the "binary point");
(2) if the whole part of the decimal number to be converted is greater than 0, convert the whole part and the fractional part of the decimal number separately into a binary number.

Check:
0.10112 = 1*2−1 + 1*2−3 + 1*2−4 = 1/2 + 1/8 + 1/16 = 11/16 = 0.687510 (ok)


The JavaScript program implementing the above algorithm is as follows:

// converting a decimal fraction to a binary one 

var x=0.6875;

writeln("Decimal fraction: "+x);
writeln();

var maxi=50;
var s="";

var i=1;
while(x>0 && i<=maxi) {
 x*=2;
 if(x<1) {
  write("   whole part: 0,");
  s+="0";
  }
 else {
  write("   whole part: 1,");
  s+="1";
  x=x-1;
  }
 writeln("   fractional part: "+x);
 if(i%4==0) {
  writeln("..... "+i+". cycle .....");
  s+=" ";
  }
 i++;
 }

writeln();
writeln("Binary fraction (first "+maxi+" binary digits): 0."+s);

writeln("__________");



Példák algoritmusokra:
4. bináris szám átalakítása hexadecimális számmá

Szótár:
– bináris számok = kettes számrendszerbeli számok
– hexadecimális számok = tizenhatos számrendszerbeli számok

1. egész szám átváltása

Példa: 1010|11012 = ?16

Két fontos dolgot tudnunk kell az algoritmus megértéséhez:

(1) Mivel a kettes számrendszerbeli (egész) számok rendszerint elég sok számjegyből állnak, érdemes a bináris számjegyeket jobbról négyes csoportokra osztani (az utolsó csoportot pedig balról kiegészíteni "vezető" nullákkal).

Például ha a 1010010110 bináris szám számjegyeit jobbról négyes csoportokra bontjuk, akkor
    0010|1001|0110
adódik.

(2) A hexadecimális számok számjegyei a decimális számjegyek (0, 1, ..., 9) mellett az A, B, C, D, E és F számjegyeket is magukba foglalják.

Hexadecimális számjegyek

A bináris egész számokat hexadecimális számokká alakító algoritmus lépései:

Eredmény: 1010|11012 = AD16


A fenti példa táblázatos formában:

1010|11012 = ?16

bin
számjegy
helyi érték szorzat hexa
számjegy
1 23=8 8
0 22=4 0
1 21=2 2
0 20=1 0
összesen 10 A
1 23=8 8
1 22=4 4
0 21=2 0
1 20=1 1
összesen 13 D

Eredmény:
1010|11012 = AD16

Ellenőrzés:
    (1) 1010|11012 = 1*27 + 0*26 + 1*25 + 0*24 + 1*23 + 1*22 + 0*21 + 1*20 = 128 + 32 + 8 + 4 + 1 = 17310 (ld. 2. algoritmus)
    (2) AD16 = 10*161 + 13*160 = 10*16 + 13*1 = 160 + 13 = 17310 (ld. 3. algoritmus)
A két szám megegyezik (ok).


A fordított irányú átváltás, azaz egy hexadecimális szám átváltása bináris számmá az előző algoritmushoz teljesen hasonlóan működik. Vegyünk egy tetszőleges hexadecimális számot, és

Példa: 2AD16 = ?2

Tehát az eredmény: 2AD16 = 0010|1010|10112


Az algoritmust megvalósító JavaScript program:

// hexadecimális szám átalakítása bináris számmá 

function hex2dec(hx) {
 var sz=-1;
 switch(hx) {
  case "a":
  case "A": sz=10; break;
  case "b":
  case "B": sz=11; break;
  case "c":
  case "C": sz=12; break;
  case "d":
  case "D": sz=13; break;
  case "e":
  case "E": sz=14; break;
  case "f":
  case "F": sz=15; break;
  default: 
   sz=parseInt(hx);  
  }
 return sz;
 }

var x="2AD";

writeln("A hexadecimális szám: "+x);
writeln();

var t=""; // végeredmény

for(var i=0;i<x.length;i++) {
 var h=hex2dec(x[i]);
 var helyiertek=8;
 var s=""; // részeredmény

 while(helyiertek>=1) {
  if(h>=helyiertek) {
   s=s+"1";
   h-=helyiertek;
   }
  else {
   s=s+"0";
   }
  helyiertek/=2;
  }

 writeln("    "+x[i]+" = "+hex2dec(x[i])+" = "+s);
 if(i>0) {
  t=t+" ";
  }
 t=t+s;
 }

writeln();
writeln(x+" bináris alakja: "+t);

writeln("____________"); 


2. törtszám átváltása

Példa: 0.1011|01012 = ?16

Mivel a kettes számrendszerbeli (tört) számok rendszerint elég sok számjegyből állnak, érdemes a bináris számjegyeket balról (0. után kezdődően) négyes csoportokra osztani (az utolsó csoportot pedig jobbról kiegészíteni "vezető" nullákkal).

Például ha a 0.10110101 bináris szám számjegyeit balról négyes csoportokra bontjuk, akkor
    0.1011|0101
adódik.

A bináris törtszámokat hexadecimális számokká alakító algoritmus lépései:

Eredmény: 0.1011|01012 = 0.B516


A fenti példa táblázatos formában:

0.1011|01012 = ?16

bin
számjegy
helyi érték szorzat hexa
számjegy
1 23=8 8
0 22=4 0
1 21=2 2
1 20=1 1
összesen 11 B
0 23=8 0
1 22=4 4
0 21=2 0
1 20=1 1
összesen 5 5

Eredmény:
0.1011|01012 = 0.B516

Ellenőrzés:
    (1) 0.1011|01012 = 1*2−1 + 1*2−3 + 1*2−4 + 1*2−6 + 1*2−8 = 1/2 + 1/8 + 1/16 + 1/64 + 1/256 = 128/256 + 32/256 + 16/256 + 4/256 + 1/256 = 181/256 = 0.7070312510
    (2) 0.B516 = 11*16−1 + 5*16−2 = 11/16 + 5/256 = 176/256 + 5/256 = 181/256 = 0.7070312510
A két szám megegyezik (ok).



Példák algoritmusokra:
5. decimális szám átalakítása hexadecimális számmá

Szótár:
– decimális számok = tízes számrendszerbeli számok
– hexadecimális számok = tizenhatos számrendszerbeli számok

Példa: 31410 = ?16

1. lépés

hányados maradék
314
314:2= 157 0
157:2= 78 1
78:2= 39 0
39:2= 19 1
19:2= 9 1
9:2= 4 1
4:2= 2 0
2:2= 1 0
1:2= 0 1

Tehát 31410 = 1|0011|10102 → 0001|0011|10102

2. lépés

bin
számjegy
helyi érték szorzat hexa
számjegy
0 23=8 0
0 22=4 0
0 21=2 0
1 20=1 1
összesen 1 1
0 23=8 0
0 22=4 0
1 21=2 2
1 20=1 1
összesen 3 3
1 23=8 8
0 22=4 0
1 21=2 2
0 20=1 0
összesen 10 A

Tehát 1|0011|10102 = 13A16

Eredmény:
31410 = 13A16

Ellenőrzés:
13A16 = 1*162 + 3*161 + 10*160 = 1*16*16 + 3*16 + 10*1 = 256 + 48 + 10 = 31410 (ok)


Egy decimális számot a következő algoritmussal egy lépésben is átalakíthatjuk hexadecimális számmá:

hányados maradék ellenőrzés
314
314:16= 19 10=A 314=16*19+10
19:16= 1 3 19=16*1+3
1:16= 0 1 1=16*0+1

Eredmény:
31410 = 13A16


A fenti algoritmust megvalósító JavaScript program:

// decimális szám átalakítása hexadecimális számmá osztással

function hex2dec(hx) {
 var sz=-1;
 switch(hx) {
  case "a":
  case "A": sz=10; break;
  case "b":
  case "B": sz=11; break;
  case "c":
  case "C": sz=12; break;
  case "d":
  case "D": sz=13; break;
  case "e":
  case "E": sz=14; break;
  case "f":
  case "F": sz=15; break;
  default: 
   sz=parseInt(hx);  
  }
 return sz;
 }

function dec2hex(sz) {
 var hx="-1";
 switch(sz) {
  case 10: hx="A"; break;
  case 11: hx="B"; break;
  case 12: hx="C"; break;
  case 13: hx="D"; break;
  case 14: hx="E"; break;
  case 15: hx="F"; break;
  default: 
   hx=""+sz;  
  }
 return hx;
 }

var x="314"; 

writeln("Decimális szám: "+x);
writeln();

var h="";
var q=1;

while(q>0) {
 q=Math.trunc(x/16); // integer division!!
 write("   Hányados: "+q); 
 var m=x-16*q;
 writeln(", maradék: "+m);
 h=dec2hex(m)+h;
 x=q;
 }

writeln();
writeln("Hexadecimális szám: "+h);

writeln("__________");



Various representation of numbers in digital computers

In digital computers, data is represented in binary notation, as sequences of bits. In the following, we assume that the data is stored in an 'n' bit storage unit called a register. In a register, 'n' different bits can be stored independently which allows a total of m=2n different bit variations. So an 'n' bit register can store 'm' different (binary coded) data. The number 'm' is also called the modulus of the register.
For example,
– the modulus of an 8 bit register is m=256;
– the modulus of a 16 bit register is m=65536;
– the modulus of a 24 bit register is m=16,777,216;
– the modulus of a 32 bit register is m=4,294,967,296.

Exactly m=2n different variations of binary coded data can be stored in an 'n' bit register.

Since we store individual bits in a register, an 'n' bit register can also be thought of as a series of one-bit storage units denoted by b0, b1, ... respectively (where each unit has a value of 0 or 1). Using such markup, a register can be described in a form of
    bn−1bn−2...b2b1b0
where bn−1 stores (or represents) the most significant bit (MSB), and b0 the least significant bit (LSB). The meaning of MSB and LSB depends on the actual represention of data, e.g. when we deal with integers represented in a binary number system, MSB corresponds to the highest positional value, and LSB corresponds to the lowest positional value (i.e. the number of ones).

Note that in the case of the representation of signed numbers (more precisely, when the sequence of bits stored in the register is interpreted as a signed number), the leftmost bit usually specifies the sign of the number:
    for bn−1=0, the number stored in the register is positive;
    for bn−1=1 the number stored in the register is negative.
In this case, the leftmost bit is called the sign bit.

As for placing the bits of an unsigned binary number in a register, it is a general rule that
– the digit having the lowest positional value of the number is stored in the rightmost or LSB position in the register (i.e. which has the lowest sequence number, e.g. b0), and accordingly,
– the digit having the highest positional value of the number is stored in the leftmost or MSB position in the register (i.e. which has the highest sequence number, e.g. bn−1).

If we store a given number in a register, the digits of the number can be assigned to the bits of the register in different ways. Accordingly, we can talk about the following coding or number representation methods:

  1. direct coding of binary numbers
  2. two's complement representation of binary numbers
  3. binary coded decimal (BCD) representation of signed or unsigned integers
  4. floating point representation of real numbers

(1) direkt kódolás

Ha egy regiszterben egy bináris számrendszerben ábrázolt nemnegatív egész számot tárolunk úgy, hogy a regiszter bitjeinek a szám bináris számjegyeit feleltetjük meg a helyi értékek megszokott sorrendjében (azaz az LSB-hez a legkisebb, 1-es helyiérték tartozik, majd (jobbról balra haladva) a következő bithez a 2-es helyiérték, utána a 4-es, 8-as stb.), akkor ún. direkt kódolásról (vagy egyenes kódolásról) beszélünk. Mivel direkt kódolás esetén csak nemnegatív számokat ábrázolunk, ezért nincs szükségünk előjelbitre.

Direkt kódolás esetén például egy 8 bites regiszterben tárolható legkisebb szám 0000|00002=010, a legnagyobb pedig 1111|11112=25510.


(2) kettes komplemens kódolás

Tároljunk egy 'n' bites (m=2n modulusú) regiszterben egész számokat a következőképpen:

Ezt a kódolást kettes komplemens kódolásnak nevezzük.

Ábrázoljunk példaként egy 8 bites regiszterben kettes komplemens kódban néhány számot (a bitsorozatok után alsó indexben megadott '2' most kettes komplemens kódban ábrázolt számot jelöl):

decimális érték kettes komplemens kód decimális érték kettes komplemens kód
010 0000|00002
110 0000|00012 −110 1111|11112
210 0000|00102 −210 1111|11102
310 0000|00112 −310 1111|11012
...
2310 0001|01112 −2310 1110|10012
...
12610 0111|11102 −12610 1000|00102
12710 0111|11112 −12710 1000|00012
−12810 1000|00002

Például −2310=1110|10012 mivel 0001|01112=1110|10002 (egyes komplemens) és ehhez 1-et binárisan hozzáadva 1110|10002+12=1110|10012 adódik.
Jegyezzük meg, hogy kettes komplemens kódban 12710 a legnagyobb ábrázolható pozitív szám, és −12810 a legkisebb ábrázolható negatív szám.

Ha képezzük a −2310+2310 összeget 0001|01112+1110|10012=1|0000|00002 adódik, ami éppen a 8 bites regiszter m=256 modulusának 2-es számrendszerben ábrázolt értéke. Általánosan is igaz, hogy egy 'n' bites regiszterben (a megengedett −m/2≤x<m/2 számtartományon belül) egy kettes komplemens kódban ábrázolt negatív szám és a vele abszolút értékben megegyező nemnegatív szám összege a regiszter modulusát adja. Formálisan kifejezve: ha a megengedett számtartományon belül 'v' egy nemnegatív egész számot jelöl, 'w' pedig ennek kettes komplemens kódját, akkor v+w=m vagy másképpen kifejezve v+w=0 (mod m) teljesül.

Két fontos szabály:

Negatív egész számokat kettes komplemens kódban ábrázolva a kivonást összeadásra tudjuk visszavezetni.

(3) binárisan kódolt decimális (BCD) számábrázolás

BCD számábrázolás esetén tízes számrendszerben megadott egész számokat kódolunk a decimális számjegyek adott számú biten történő bináris megadásával. A BCD számábrázolás két típusa:
– pakolt, csomagolt vagy tömörített ("packed") BCD esetén a decimális számjegyeket 4 biten (1 tetrádon) ábrázoljuk (egy bájt felső vagy alsó 4 bitje egy ún. fél bájt vagy tetrád, angolul "nibble");
– pakolatlan, kibontott vagy zónázott ("unpacked") BCD esetén a decimális számjegyeket 8 biten (1 bájton) ábrázoljuk (a felső 4 bitet az ún. zónabitek alkotják, az alsó 4 bit pedig az ábrázolandó decimális számjegy bináris alakját tartalmazza).



Számábrázolás a számítógépen: lebegőpontos számábrázolás

Lebegőpontos számábrázolás esetén normál alakban megadott valós számokat kódolunk adott pontossággal. Ennek alapja az, hogy az ábrázolandó 'x' valós számot
x = s*m*2k
normál alakban adjuk meg, ahol 's' a szám előjele (s=±1), 'm' a kettedes tört formában megadott ún. mantissza (m∈ℝ), és 'k' az ún. karakterisztika (k∈ℤ).

A mantissza számjegyeinek (bitjeinek) a száma a számábrázolás pontosságát, a karakterisztika pedig az ábrázolható számok nagyságrendjét határozza meg (vö. Nyakóné Juhász 2011: 21).

A lebegőpontos számok ábrázolása

A továbbiakban az egyszeres pontosságú, 32 bites lebegőpontos számábrázolással foglalkozunk. (Az Institute of Electrical and Electronics Engineers (IEEE) által a nyolcvanas években kiadott IEEE 754 nevű szabvány alapján, vö. Nyakóné Juhász 2011: 21-22).

Ha egyszeres pontossággal, 32 biten ábrázolunk egy lebegőpontos számot, akkor
– az előjelet a bal szélső 1 biten a szokásos módon,
– a karakterisztikát a következő 8 biten többletes kódolással,
– a mantisszát pedig a fennmaradó 23 biten fixpontos kódolással
ábrázoljuk.

b b b ... b b b b b b b ... b b b
előjel (sign, 1 bit)
karakterisztika (exponent, 8 bit)
mantissza (fraction, 23 bit)

Jelöljük
– az ábrázolt valós számot 'x'-szel;
– a karakterisztika tényleges értékét 'k'-val, az egyszeres pontosságú lebegőpontos számnak a karakterisztika számára fenntartott 8 bitjén direkt kódolással ábrázolt "többletes" (127-tel, ill. kis számok esetén 126-tal eltolt) értéket pedig exp-val;
– a mantissza tényleges értékét 'm'-mel, az egyszeres pontosságú lebegőpontos számnak a mantissza számára fenntartott 23 bitjén fixpontos kódolással ábrázolt értéket pedig frac-val.

Három esetet különböztetünk meg:

(1) "normál" eset: a karakterisztika legalább −126, és az ábrázolt exponens legalább 1 (vagyis nem zérus)

Ha az ábrázolandó valós számra 2−126≤|x|<2128 teljesül, akkor

Ha a karakterisztika (k) kódolt értéke nem zérus (vagyis a karakterisztika −126-nál nem kisebb egész szám), a mantissza tényleges értékét (m) egy olyan kettedes törttel fejezzük ki, amelynek egész része mindig 1 (vagyis a mantissza 1≤m<2 közötti valós szám). Az egyszeres pontosságú lebegőpontos számnak a mantissza számára fenntartott 23 bitje ilyenkor a mantissza tört részének bináris számjegyeit adja meg.

Megjegyzések:
– a karakterisztika tényleges értékének (k) fenti módon történő kódolását ún. 127-es többletes kódolásnak nevezzük (ez k+127 direkt kódban történő ábrázolását jelenti adott számú (ti. 8) biten);
– a kettedestörtek számjegyeinek (az egész résznek és/vagy a tört résznek) adott számú biten történő ábrázolását nevezzük ún. fixpontos számábrázolásnak (a mantissza ábrázolása ezt a kódolási formát követi, mivel a mantissza tört részének bináris számjegyeit rögzített számú (ti. 23) biten adjuk meg);
– az (1) esetben ábrázolható legkisebb szám esetén exp=1 és frac=0, tehát
    x(1),min=1.0000...*2−126=2−126;
– az (1) eset ekvivalens azzal, amikor a karakterisztika −125≤k≤128 közötti egész szám és a mantissza 1/2≤m<1 közötti valós szám (vagyis a mantisszát 0.1-re normáljuk). Ekkor a karakterisztikát 8 biten 126-os többletes kódban adjuk meg, azaz az exp=k+126 egész számot ábrázoljuk direkt kódban (1≤exp≤254), és a mantissza kettedestört alakjában a mantissza 0.1 utáni bináris számjegyeit ábrázoljuk a rendelkezésre álló 23 biten.

(2) a zérushoz közeli ("kis") számok esete: a karakterisztika −126, és az ábrázolt exponens zérus

Ha az ábrázolandó valós számra 0≤|x|<2−126 teljesül, akkor

Ha a karakterisztika (k) kódolt értéke zérus (vagyis a karakterisztika értéke −126), a mantissza tényleges értékét (m) egy olyan kettedes törttel fejezzük ki, amelynek egész része mindig 0 (vagyis a mantissza 0≤m<1/2 közötti valós szám). Az egyszeres pontosságú lebegőpontos számnak a mantissza számára fenntartott 23 bitje ilyenkor is a mantissza tört részének bináris számjegyeit adja meg. Ha a mantissza értéke zérus, akkor a lebegőpontos szám értéke is zérus (az előjeltől függetlenül).

Megjegyzések:
– ha az (1) esetben a mantisszát 0.1-re normáljuk (azaz 1/2≤m<1 teljesül), akkor a (2) eset annak a speciális esetnek felel meg, amikor a karakterisztika k=−126 (és exp=0); de mivel szeretnénk a zérust is ábrázolni, most megengedjük az 1/2-nél kisebb nemnegatív mantisszákat is (vagyis ebben az esetben 0≤m<1 teljesül);
– a (2) esetben alkalmazott kódolás lehetővé teszi a zérus ábrázolását ("lebegőpontos nulla"), sőt valójában ez kétféleképpen is lehetséges: ha az egyszeres pontosságú lebegőpontos számok számára rendelkezésre álló 32 biten csupa 0 szerepel, "pozitív" zérusról, ha pedig az első bit 1, és utána 31 biten csupa 0 szerepel, "negatív" zérusról beszélhetünk;
– a (2) esetben ábrázolható legnagyobb szám m=0.1111... és exp=0 (azaz k=−126) miatt
    x(2),max=0.1111...*2−126≲2−126.

(3) túlcsordulás: a karakterisztika legalább 128, és az ábrázolt exponens 255 (azaz binárisan 1111|1111) (az abszolút értékben "túlságosan" nagy, ezért lebagőpontosan már nem ábrázolható számok esete)

Ha az ábrázolandó számra 2128≤|x| teljesül, akkor a valós szám már nem ábrázolható az egyszeres pontosságú lebegőpontos formátumban. Ekkor

Túlcsorduláskor az ábrázolt bitsorozatot a mantissza értékétől függetlenül nem számként értelmezzük, hanem az előjelbittől függően plusz, ill. minusz végtelenként (±∞). A túlcsordulás szokásos jelölése NaN ("not a number").

1. példa (vö. Nyakóné Juhász 2011: 22): Tekintsük az alábbi 32 bites bitsorozatot, amely egy egyszeres pontosságú lebegőpontos számot határoz meg:
    x=1 10000111 101000000000000000000002
Határozzuk meg, ez milyen valós számnak felel meg!

A lebegőpontos számábrázolás (1) esetét alapul véve
– mivel az előjelbit 1, ezért 'x' negatív;
– mivel k'=1000|01112=13510, ezért a karakterisztika értéke k=k'−127=8;
– mivel az 1-re normált mantissza m=1.101000...2, ezért
m=1+1/2+1/8=8/8+5/8=13/8

Tehát a keresett valós szám 28=256 miatt
x=−13/8*28=−13/8*256=−416

Vizsgáljuk meg azt az esetet, amikor a mantisszát 0.1-re normáljuk. Ekkor a következőket kapjuk:
– mivel az előjelbit 1, ezért 'x' negatív;
– mivel k'=1000|01112=13510, ezért a karakterisztika értéke k=k'−126=9;
– mivel a 0.1-re normált mantissza m=0.1101000...2, ezért
m=1/2+1/4+1/16=8/16+4/16+1/16=13/16
Tehát a keresett valós szám 29=512 miatt
x=−13/16*29=−13/16*512=−416
megegyezik az előző értékkel.


2. példa: Tekintsük az alábbi 32 bites bitsorozatot, amely most is egy egyszeres pontosságú lebegőpontos számot határoz meg:
    x=0 00000000 101000000000000000000002
Határozzuk meg, ez (közelítőleg) milyen valós számnak felel meg!

A lebegőpontos számábrázolás (2) esetét alapul véve
– mivel az előjelbit 0, ezért 'x' pozitív;
– mivel k'=0000|00002=010, ezért a karakterisztika értéke k=k'−126=−126;
– mivel a 0.-val kezdődő mantissza m=0.101000...2, ezért
m=1/2+1/8=5/8

Tehát a keresett valós szám
x=5/8*2−126≈7.3468...10−39


Alapműveletek kettes számrendszerben

Alapműveletek:

1. összeadás és kivonás

Mivel a kettes számrendszerben csak két számjegyünk van, az egy számjegyből álló kettes számrendszerbeli számok összeadásához az alábbi táblázatra van szükségünk:

+ 0 1
0 0 1
1 1 10

A táblázatból látható, hogy 12+12=102 vagyis két 1-es összeadása két számjegyből álló számot eredményez. Ezt úgy is megfogalmazhatjuk, hogy két 1-es összeadásakor az alacsonyabb helyiértékről ún. átvitel keletkezik a magasabb helyiértékre.

Mivel az azonos helyiértékű számjegyek összeadásakor az átvitelt is figyelembe kell vennünk, érdemes a fenti táblázatot bővíteni egy harmadik oszloppal, amelyben az átvitelt jelentő bitek szerepelnek:

összeadandó bitek eredmény
0 0 0 0
1 0 0 1
0 1 0 1
0 0 1 1
1 1 0 10
1 0 1 10
0 1 1 10
1 1 1 11

Lássunk ezek után néhány példát bináris számok összeadására. (A kivonást a negatív egész számok kettes komplemens kódban történő ábrázolásával összeadásra tudjuk visszavezetni.)


Legyenek 'p', 'q' és 'r' 8 bites regiszterek, amelyekben a számokat direkt kódban ábrázoljuk.

Példa összeadásra:
Legyen p=0010|10112 és q=0011|10102; r=p+q=?2

r=p+q
p =  0 0 1 0 | 1 0 1 1
q =  0 0 1 1 | 1 0 1 0
átvitel 0 1 1 1 | 0 1 0 0
r =  0 1 1 0 | 0 1 0 1

Eredmény: r=0110|01012=10110

Ellenőrzés: p=4310, q=5810, r=10110, vagyis p+q=r teljesül, tehát jól számoltunk.

Ha direkt kódban ábrázolt bináris számok összeadásakor az utolsó (legmagasabb helyiértékű) biten átvitel keletkezik, akkor a két szám összege túl nagy lesz, ezért már nem ábrázolható az 'r' regiszterben. Ilyen esetekben túlcsordulásról beszélünk.


Példa kivonásra:
Legyen p=0110|10112 és q=0011|01102; r=p−q=?2

r=p−q
p =  0 1 1 0 | 1 0 1 1
q =  0 0 1 1 | 0 1 1 0
átvitel 0 1 1 0 | 1 0 0 0
r =  0 0 1 1 | 0 1 0 1

Megjegyzés: az átvitel sorban szereplő biteket a felette levő sor (a 'q' kivonandó) bitjeihez kell hozzáadni.

Eredmény: r=0011|01012=5310

Ellenőrzés: p=10710, q=5410, r=5310, vagyis p−q=r teljesül, tehát jól számoltunk.


Legyenek 'p', 'q' és 'r' ismét 8 bites regiszterek, amelyekben a számokat kettes komplemens kódban ábrázoljuk.

Példa:
Legyen p=1010|10112 és q=0001|10102; r=p+q=?2

p =  1 0 1 0 | 1 0 1 1
q =  0 0 0 1 | 1 0 1 0
átvitel 0 1 1 1 | 0 1 0 0
r =  1 1 0 0 | 0 1 0 1

Eredmény: r=1100|01012

Ellenőrzés:
– mivel 'p' negatív, |p|=0101|01002+12=0101|01012=8510 tehát p=−8510;
– q=0001|10102=2610;
– mivel 'r' is negatív, |r|=0011|10102+12=0011|10112=5910 tehát r=−5910.
Mivel p=−8510, q=2610, r=−5910, ezért p+q=r teljesül, tehát jól számoltunk. Az elvégzett összeadás egyenértékű a 2610−8510=−5910 kivonással, amit a példában a negatív számokat kettes komplemens kódban ábrázolva összeadásra vezettünk vissza.


2. szorzás

Direkt kódban ábrázolt kettes számrendszerbeli számok esetén a tizes számrendszerben jól ismert szorzási algoritmust alkalmazhatjuk. Jegyezzük meg a következőket:


Legyenek 'p', 'q' és 'r' 8 bites regiszterek, amelyekben a számokat direkt kódban ábrázoljuk.

Példa szorzásra:
Legyen p=0000|10112 és q=0000|10102; r=p*q=?2

p*q =  0 0 0 0 1 0 1 1 * 00001010
+ 0 0 0 0 1 0 1 1
0 0 0 0 0 0 0 0
0 0 0 0 1 0 1 1
0 0 0 0 0 0 0 0
r =  0 0 0 0 1 1 0 1 1 1 0

Ha a szorzó kettőnél több 1-est tartalmaz akkor a táblázatban kettőnél több kettes számrendszerbeli számot kell összeadnunk. Ilyenkor a táblázatba több segédsort is beírhatunk, amelyek az egyes részösszegeket (és szükség esetén az ezekhez tartozó átviteleket) tartalmazzák.

Eredmény: r=0110|11102=11010

Ellenőrzés: p=1110, q=1010, r=11010, vagyis p*q=r teljesül, tehát jól számoltunk.

Ha a szorzat túl nagy, és ezért már nem ábrázolható a 8 bites 'r' regiszterben, túlcsordulásról beszélünk.


3. osztás

Direkt kódban ábrázolt kettes számrendszerbeli számok esetén a tizes számrendszerben jól ismert osztási algoritmust alkalmazhatjuk.

Mivel az osztó csak 0 és 1 számjegyekből áll, az előző lépésben elvégzett kivonás maradékából és az osztó kiválasztott ("hozzáírt") számjegyéből kivonandó részletszorzatok vagy csupa zérusból állnak, vagy magából a szorzandóból.


Legyenek 'p', 'q' és 'r' 8 bites regiszterek, amelyekben a számokat direkt kódban ábrázoljuk.

Példa osztásra:
Legyen p=1100|10112 és q=0000|10102; r=p/q=?2

p/q =  1 1 0 0 1 0 1 1 : 1 0 1 0 = 1 0 1 0 0
1 1 0 0 0 0 0 0 0 0
1 0 1 0
0 0 1 0 1
0 0 0 0
0 1 0 1 0
1 0 1 0
0 0 0 0 1
0 0 0 0
0 0 0 1 1
0 0 0 0
d =  0 0 1 1

Ha az osztó (q) nincs meg maradék nélkül az osztandóban (p), akkor maradék képződik (d), amit a táblázat utolsó sora mutat. Ilyenkor maradékos osztást hajtottunk végre.

Eredmény:
    az osztó: r=0001|01002=2010
    a maradék: d=0000|00112=310

Ellenőrzés: p=20310, q=1010, r=2010, d=310, vagyis p=q*r+d teljesül, tehát jól számoltunk.



Calculation exercises

Általánosan egy 'b' alapszámú számrendszerben a számok a következő formában írhatók le ("ábrázolhatóak"):
egy szám általános alakja a 'b' alapú számrendszerben
A képletben

Például legyen az ábrázolt szám 314 a tízes számrendszerben (ezt 31410 módon jelölhetjük, ha egyszerre több számrendszerrel is dolgozunk). A fenti képlet alapján a számot
31410 = 3*102+2*101+4*100 = 300+20+4
formában írhatjuk fel.

Egy másik példaként legyen az ábrázolt szám 2.718 a tízes számrendszerben. A fenti képlet alapján a számot
2.71810 = 2*100+7*10−1+1*10−2+8*10−3 = 2+7/10+1/100+8/1000
formában írhatjuk fel.

Az alábbiakban a számrendszerek közötti átváltást öt típusfeladat segítségével gyakorolhatjuk:

  1. hexadecimális szám átalakítása decimális számmá
  2. bináris szám átalakítása decimális számmá
  3. decimális szám átalakítása bináris számmá
  4. bináris szám átalakítása hexadecimális számmá
  5. decimális szám átalakítása hexadecimális számmá

(1) hhh16 → ddd10

nagy , közepes , vagy kis hexadecimális szám átváltása decimális számmá

Például 1ab16 = 1*162 + a*161 + b*160 = 1*256 + 10*16 + 11*1 = 42710 (megjegyzés: a = 10, b = 11)

Segítség: táblázatok

hexadecimális szám: decimális szám: eredmény:

16  =   0   =   0   =  10

(2) bbb2 → ddd10

nagy , közepes , vagy kis bináris szám átváltása decimális számmá

Például 1011012 = 1*25 + 1*23 + 1*22 + 1*20= 1*32 + 1*8 + 1*4 + 1*1 = 4510 (megjegyzés: 25 = 32, 23 = 8)

Segítség: táblázatok, számológép

bináris szám: decimális szám: eredmény:

16  =   0   =   0   =  10
2  =   0 
 0 
 0 
 0 
 0 
10

Egy adott számrendszerbeli számot átválthatunk decimális számmá az ún. Horner-elrendezést használva is. Legyen például 111001102 az átváltandó szám.

1 1 1 0 0 1 1 0
2 1 2*1+1=3 2*3+1=7 2*7+0=14 2*14+0=28 2*28+1=57 2*57+1=115 2*115+0=230

A Horner-táblázat felépítése:
– a táblázat két sorból áll;
– a táblázat első sora a második cellától kezdve az átváltandó szám számjegyeit tartalmazza;
– a táblázat második sorának első cellájába az átváltandó szám számrendszerének alapja kerül;
– a táblázat második sorának második cellájába a felette levő számjegyet másoljuk be.

Az algoritmus lényege, hogy
– a táblázatban balról jobbra haladva és
– a második sor harmadik cellájától kezdve egymás után kitöltjük a táblázat második sorának celláit.
Az egyes cellák értékét úgy számítjuk ki, hogy
– az előző cella értékét (pl. 1) szorozzuk a számrendszer alapjával (pl. 2-vel) és
– a szorzathoz hozzáadjuk a cella felett (az első sorban) levő cellában levő számjegyet (pl. 1-et).

Az első sorban ábrázolt, megadott számrendszerbeli szám tízes számrendszerbeli értékét a második sor utolsó (jobb szélső) cellájának értéke adja.

Próbáljuk ki egy tetszőleges (2-16 alapú) számrendszerbeli, max. 8 számjegyből álló szám megadásával a Horner-elrendezést:

A számrendszerbeli szám:

1 1 1 0 0 1 1 0
2 1 3 7 14 28 57 115 230

(3) ddd10 → bbb2

nagy , közepes , vagy kis decimális szám átváltása bináris számmá

Például 7310 = 1*64 + 9 = 1*64 + 1*8 + 1 = 1*26 + 1*23 + 1*20 = 10010012 (megjegyzés: 26 = 64, 23 = 8)

Segítség: táblázatok, számológép

decimális szám: bináris szám: eredmény:

Egy decimális szám kettes számrendszerbeli alakját megkaphatjuk úgy, hogy
   (1) a számot elosztjuk 2-vel, és leírjuk az osztás eredményeként kapott hányadost és maradékot;
   (2) a leírt hányadost elosztjuk 2-vel, és ismét leírjuk az osztás eredményeként kapott hányadost és maradékot;
   (3) és ezt addig folytatjuk, míg a hányados értéke 0 nem lesz.
A kettes számrendszerbeli alakot a kapott maradékok szolgáltatják, fordított sorrendben felírva (azaz az utoljára kapott maradék lesz a kettes számrendszerbeli szám legmagasabb helyi értékű számjegye).

hányados osztás maradék
 

(4) bbb2 → hhh16

nagy , közepes , vagy kis bináris szám átváltása hexadecimális számmá

Például 10011012 = 010011012 → 01002 | 11012 → 410 | 1310 → 416 | d16 = 4d16 (megjegyzés: 11012 = 1310 = d16)

Segítség: táblázatok, számológép

bináris szám: hexadecimális szám: eredmény:


(5) ddd10 → hhh16

nagy , közepes , vagy kis decimális szám átváltása hexadecimális számmá:

Például 10410 = 6*16 + 8 = 6*161 + 8*160 = 6816

Segítség: táblázatok, számológép

decimális szám: hexadecimális szám: eredmény:


számológép

átváltandó decimális szám:  

bináris helyi érték és számjegy
hexadecimális érték
összeg
2048 1024 512 256 128 64 32 16 8 4 2 1
256 16 1

Segédtáblázatok

(1) a hexadecimális számrendszer számjegyei

hexadecimális számjegy
decimális érték
bináris érték

(2) 2 hatványai

hatvány érték

(3) 16 hatványai

hatvány érték

további információk:
Számrendszer - Wikipédia. (2021-02-21)
Számrendszerek az informatikában - Wikipédia. (2021-02-21)




Boda István, 2022.