# Codifica numeri

Gli esseri umani utilizzano un sistema di numerazione che si dice decimale, a base 10, avendo dieci cifre (da 0 a 9). I computer utilizzano invece un sistema si numerazione binario (base 2), dovuto al fatto che sono costituiti da circuiti (i transistor) con solo due stati possibili: `on` e `off`. In informatica utilizziamo anche il sistema ottale (base 8) e esadecimale (base 16), come forme compatte per rappresentare i numeri binari.

### PREREQUISITI

[https://checksound.gitbook.io/tecnologie1/rappresentazione-dei-numeri](https://checksound.gitbook.io/tecnologie1/rappresentazione-dei-numeri)

### NUMERI ESPRESSI IN DIFFERENTI BASI

Spiegare conversione da numero espresso in base 10 a numero espresso in base N: algoritmo divisioni per N tenuto conto del resto.

Spiegazione della regola (vedi allegato)&#x20;

{% file src="https://875337804-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lt58INaXg-cFL27pnx0%2F-Lu838875ONA5VjeZQ0o%2F-Lu83XM_rguxcEvsK02f%2FConversione_di_bae.pdf?alt=media&token=adfd5fc5-fa6a-4115-a982-b42f646b8741" %}
Spiegazione su conversioni di base
{% endfile %}

Vediamo un esempio, consideriamo il numero 345 in base 10 e cerchiamo la rappresentazione in base 2. Si ha allora:

```
345/2 = 172 resto 1
172/2 = 86 resto 0
 86/2 = 43 resto 0
 43/2 = 21 resto 1
 21/2 = 10 resto 1
 10/2 = 5 resto 0
  5/2 = 2 resto 1
  2/2 = 1 resto 0
  1/2 = 0 resto 1
```

Leggendo le cifre dal basso verso l'alto (in quanto si ottengono a partire dalla meno significativa), si ha la rappresentazione binaria: `101011001`

### Conversione da Decimale (Base 10) a Base r

Usa la divisione/resto ripetutamente. Per esempio,

```
To convert 261(base 10) to hexadecimal:
  261/16 => quotient=16 remainder=5
  16/16  => quotient=1  remainder=0
  1/16   => quotient=0  remainder=1 (quotient=0 stop)
  Hence, 261D = 105H (Collect the hex digits from the remainder in reverse order)
```

La procedura spiegata prima è applicabile per la conversione tra due differenti basi. Per esempio,

```
To convert 1023(base 4) to base 3:
  1023(base 4)/3 => quotient=25D remainder=0
  25D/3          => quotient=8D  remainder=1
  8D/3           => quotient=2D  remainder=2
  2D/3           => quotient=0   remainder=2 (quotient=0 stop)
  Hence, 1023(base 4) = 2210(base 3)
```

RAPPORTO TRA NUMERI BINARI - OTTALI - ESADECIMALI

### DA BINARIO A OTTALE e VICEVERSA

Si osservi che ogni cifra del sistema ottale può essere rappresentata con una tripletta di bit come mostrato nella seguente tabella:

| BINARIO | OTTALE |
| ------- | ------ |
| 000     | 0      |
| 001     | 1      |
| 010     | 2      |
| 011     | 3      |
| 100     | 4      |
| 101     | 5      |
| 110     | 6      |
| 111     | 7      |

Regola pratica:

#### Da binario a ottale

Si raggruppano le cifre del numero binario a gruppi di tre a partire da destra, e si trasformano le cifre di ciascun gruppo nel corrispondente numero ottale.

Dato il binario 1011110111

Si può scrivere come:

| 1 | 011 | 110 | 111 |
| - | --- | --- | --- |
| 1 | 3   | 6   | 7   |

quindi: 1011110111 base 2 = 1367 base 8

#### Da ottale a binario

Si fa corrispondere a ciascuna delle cifre ottali che compongono il numero un gruppo di tre bit.

Per esempio, dato il numero ottale 625

si può scrivere&#x20;

| 6   | 2   | 5   |
| --- | --- | --- |
| 110 | 010 | 101 |

quindi: 625 base 8 = 110010101 base 2

### DA BINARIO A ESADECIMALE e VICEVERSA

| BINARIO | ESADECIMALE |
| ------- | ----------- |
| 0000    | 0           |
| 0001    | 1           |
| 0010    | 2           |
| 0011    | 3           |
| 0100    | 4           |
| 0101    | 5           |
| 0110    | 6           |
| 0111    | 7           |
| 1000    | 8           |
| 1001    | 9           |
| 1010    | A           |
| 1011    | B           |
| 1100    | C           |
| 1101    | D           |
| 1110    | E           |
| 1111    | F           |

#### Da binario a esadecimale

Si raggruppano le cifre del numero per gruppi di 3 a partire da destra, e si trasformano le cifre di ciascun gruppo nel corrispondente numero esadecimale, secondo la tabella di conversione.

Per esempio, il numero binario 1011110111

si può scrivere come

| 10 | 1111 | 0111 |
| -- | ---- | ---- |
| 2  | F    | 7    |

quindi 1011110111 base 2 = 2F7 base 16

#### Da esadecimale a binario

Si fa corrispondere a ciascuna delle cifre esadecimali che compongono il numero un gruppo di 4 bit secondo la tabella di conversione.

Per esempio, dato un numero esadecimale,

`C3B`

| C    | 3    | B    |
| ---- | ---- | ---- |
| 1100 | 0011 | 1011 |

quindi: C3B base 16 = 110000111011 base 2

### OPERAZIONI TRA NUMERI IN BASE BINARIA

Data la rappresentazione binaria dei numeri, un problema interessante è quello di capire come possano essere effettuate le usuali operazioni aritmetiche su tali numeri. Nel caso della rappresentazione decimale ci è stato insegnato fin da piccoli come fare le operazioni e abbiamo quindi un procedimento automatico per eseguirle. Si pensi, ad esempio, alla operazione di addizione: il nostro procedimento automatico prevede di mettere in colonna i numeri da sommare e quindi prevede delle ben precise regole di somma con l'effettuazione di riporto ogni volta che una somma parziale supera il valore 9.

Lo stesso identico procedimento automatico di addizione può essere utilizzato anche nel caso di operazioni in base 2. L'unica differenza è che in questo caso si devono effettuare riporti nel caso in cui la somma parziale supera 1. Avremo quindi la seguente tabella di regole di somma parziale da utilizzare nell'eseguire le nostre operazioni:

```
0 + 0 = 0 con riporto 0
0 + 1 = 1 con riporto 0
1 + 0 = 1 con riporto 0
1 + 1 = 0 con riporto 1
```

Vediamo qualche semplice esempio:

```
 1 +                  101 +                10110101 +
 1 =                   11 =                 1000110 =
------          --------------         ----------------
10                   1000                  11111011
```

&#x20;In questo modo si possono continuare ad usare gli stessi procedimenti imparati per l'aritmetica decimale per eseguire una qualunque operazione. Nel seguito non analizzeremo in dettaglio il problema, ma si può verificare che anche i procedimenti per eseguire sottrazioni o prodotti ( e tutte le altre operazioni) possono essere applicati in modo naturale.

Un aspetto importante dell'aritmetica dei calcolatori è invece il fatto che in alcuni casi si può avere un errore come risultato di un'operazione. In particolare, il problema può sorgere nel caso in cui la somma di due numeri rappresentabili con il numero di bit prescelto non sia rappresentabile e quindi abbia un errore di **overflow** (letteralmente "straripamento"). Supponiamo di avere numeri rappresentabili mediante 8 bit e di eseguire la seguente somma:

```
  10110101 +
  11011110 =     OVERFLOW
--------------
 110010011 
```

Si ha un errore di overflow in quanto il risultato della somma non è rappresentabile con 8 bit ma ne richiede almeno 9. In situazioni di questo tipo si ha un errore nell'esecuzione dell'operazione e l'arresto dell'elaborazione.

Quindi:

```
 1 +
 1
-----
10
```

e

```
 1 +
 1 +
 1
-----
11
```

### ESEMPIO DI OPERAZIONI TRA NUMERI IN BASE 4

A mo' di esempio, vediamo le operazioni scegliendo numerazione base 4.

```
 2 +
 2
----
10
```

```
 2 +
 3 
----
11
```

### LA RAPPRESENTAZIONE DEI NUMERI NON INTERI

Una delle più grandi difficoltà matematiche dell’antichità fu la gestione delle quantità non intere: la civiltà egizia impiegava esclusivamente le frazioni 1/2, 1/4, 1/8, 1/16, 1 /32 e 1/64.&#x20;

La civiltà babilonese affrontò il problema con l’uso intensivo dei numeri 12, 60 e 360, che ancora oggi sono ricorrenti in molti campi dell’attività umana (la «dozzina», i minuti e i secondi, la ripartizione in gradi del cerchio). Questi numeri risultano divisibili per molti divisori distinti e, di conseguenza, evitano il ricorso a quantità non intere nella maggior parte dei casi di interesse pratico.&#x20;

La rappresentazione dei **numeri decimali** – una delle modalità moderne per rappresentare quantità non intere – estende la struttura posizionale dei numeri interi: dopo il simbolo «,» le posizioni assumono il valore delle potenze crescenti3 di 1 /10 secondo lo schema di FIGURA sotto.

![](https://875337804-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lt58INaXg-cFL27pnx0%2F-MLJEaDmD3gUfCOyqHG5%2F-MLJMmiM-0HOw8UKN5AK%2Ffrazionari_decimali.PNG?alt=media\&token=9ebae7fb-469d-4709-9b12-8f95f2d61158)

È certamente possibile rappresentare anche i numeri binari non interi con questa tecnica: in questo caso le posizioni successive al segno «,» rappresentano potenze crescenti di 1/2.

![](https://875337804-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lt58INaXg-cFL27pnx0%2F-MLJEaDmD3gUfCOyqHG5%2F-MLJNrEvtIyg7OETImy0%2Ffrazionari_binari.PNG?alt=media\&token=3366665a-81db-4fab-ac61-325a40c59131)

**ESEMPIO:** Il numero binario 10,1101 (base 2) rappresenta il valore decimale&#x20;

`1 × 2 + 0 × 1 + 1 × 1/2 + 1 × 1/4 + 0 × 1/8 + 1 × 1/16 = 2 + 0 + 0,5 + 0,25 + 0 + 0,0625 = 2,8125 (base 10)`

### LA CONVERSIONE TRA DUE BASI CON PARTE FRAZIONARIA

1. Si separa la parte intera da quella frazionaria;
2. Per la parte intera, si divide per la base di conversione ripetutamente, e si collezionano i resti nell'ordine inverso;
3. Per la parte frazionaria, si moltiplica la parte frazionaria per la base di destinazione ripetutamente, e si raccoglie la parte intera nello stesso ordine.

{% tabs %}
{% tab title="Da decimale a binario" %}
```
Convert 18.6875D to binary
Integral Part = 18D
  18/2 => quotient=9 remainder=0
  9/2  => quotient=4 remainder=1
  4/2  => quotient=2 remainder=0
  2/2  => quotient=1 remainder=0
  1/2  => quotient=0 remainder=1 (quotient=0 stop)
  Hence, 18D = 10010B
Fractional Part = .6875D
  .6875*2=1.375 => whole number is 1
  .375*2=0.75   => whole number is 0
  .75*2=1.5     => whole number is 1
  .5*2=1.0      => whole number is 1
  Hence .6875D = .1011B
Combine, 18.6875D = 10010.1011B
```
{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Da decimale a esadecimale" %}
```
Convert 18.6875D to binary
Integral Part = 18D
  18/2 => quotient=9 remainder=0
  9/2  => quotient=4 remainder=1
  4/2  => quotient=2 remainder=0
  2/2  => quotient=1 remainder=0
  1/2  => quotient=0 remainder=1 (quotient=0 stop)
  Hence, 18D = 10010B
Fractional Part = .6875D
  .6875*2=1.375 => whole number is 1
  .375*2=0.75   => whole number is 0
  .75*2=1.5     => whole number is 1
  .5*2=1.0      => whole number is 1
  Hence .6875D = .1011B
Combine, 18.6875D = 10010.1011B
```
{% endtab %}
{% endtabs %}

**OSSERVAZIONE:** L’eventuale periodicità delle cifre che seguono il segno «,» nella rappresentazione di un numero dipende dalla base di rappresentazione; per esempio la frazione &#x31;**/**&#x35; è rappresentata in base 10 come 0,2 e in binario come 0,001100110011…

Verifica come esercizio la rappresentazione binaria di 0,2

**Exercises (Number Systems Conversion)**

1. Convert the following _decimal_ numbers into _binary_ and _hexadecimal_ numbers:
   1. `108`
   2. `4848`
   3. `9000`
2. Convert the following binary numbers into hexadecimal and decimal numbers:
   1. `1000011000`
   2. `10000000`
   3. `101010101010`
3. Convert the following hexadecimal numbers into binary and decimal numbers:
   1. `ABCDE`
   2. `1234`
   3. `80F`
4. Convert the following decimal numbers into binary equivalent:
   1. `19.25D`
   2. `123.456D`

Answers: You could use the Windows' Calculator (`calc.exe`) to carry out number system conversion, by setting it to the _Programmer_ or _scientific_ mode. (Run "calc" ⇒ Select "Settings" menu ⇒ Choose "Programmer" or "Scientific" mode.)

1. `1101100B`, `1001011110000B`, `10001100101000B`, `6CH`, `12F0H`, `2328H`.
2. `218H`, `80H`, `AAAH`, `536D`, `128D`, `2730D`.
3. `10101011110011011110B`, `1001000110100B`, `100000001111B`, `703710D`, `4660D`, `2063D`.
4. ?? (You work it out!)

### RANGE VALORI CON RAPPRESENTAZIONE DEGLI INTERI SENZA SEGNO

| N° bit usati | VALORE MINIMO | VALORE MASSIMO                                           |
| ------------ | ------------- | -------------------------------------------------------- |
| 8            | 0             | `(2^8) - 1` (= 255)                                      |
| 16           | 0             | `(2^16) - 1` (= 65.535)                                  |
| 32           | 0             | `(2^32) - 1` (= 4.294.967.295) (9+ digits)               |
| 64           | 0             | `(2^64) - 1` (= 18.446.744.073.709.551.615) (19+ digits) |

### INTERI CON SEGNO

Finora abbiamo visto come nel sistema binario sia possibile rappresentare i numeri naturali, ossia i numeri interi positivi (compreso lo 0). In genere tali numeri non sono sufficienti per eseguire elaborazioni matematiche ed è necessario anche avere la possibilità di rappresentare i numeri negativi (e quindi i numeri interi) e i numeri con la virgola. Ora affronteremo in questo paragrafo il primo di tali problemi, mentre al secondo (e al problema della rappresentazione di numeri grandi, che generano overflow nella rappresentazione prescelta), sarà dedicato il prossimo paragrafo.

Si supponga di utilizzare 4 bit per la rappresentazione dei numeri. Secondo la rappresentazione binaria discussa finora, in tal modo è possibile rappresentare i numeri da 0 a 2 ^ 4 - 1, ossia tra 0 e 15.

Qualora in accordo con il fatto che vi sono 2 ^ 4 possibili configurazioni disponibili con 4 bit. **Tali configurazioni potrebbero essere utilizzate invece che per codificare solo i numeri positivi da 0 a 2 ^ 4 -1 anche per rappresentare anche i numeri negativi**. In altri termini invece di prendere l'insieme di 2 ^ 4 elementi a partire da 0, si può pensare di prendere un insieme di 2 ^ 4 numeri a cavallo dello 0, da -2^(4-1) a 2^(4-1) (si noti che 2 ^ (4-1) = (2^4)/2).

Il problema è quello di decidere come possa essere ottenuto tale obiettivo. Supponiamo di utilizzare N bit per la rappresentazione dei numeri; una prima idea è quella di destinare un bit per rappresentare il segno del numero e gli altri bit per rappresentare il valore assoluto del numero. Convenzionalmente, ad esempio, possiamo decidere che il primo bit (il bit più a sinistra) denota il segno (0 per numeri positivi e 1 per i numeri negativi) e i restanti il numero in valore assoluto.

Una prima soluzione potrebbe essere quella di usare tali bit restanti per rappresentare la codifica binaria del numero sia nel caso dei numeri positivi che nel caso dei numeri negativi. Consideriamo, ad esempio, il caso in cui si usino 4 bit, allora avremmo:

```
0000 -> +0                       1000 -> -0
0001 -> +1                       1001 -> -1
0010 -> +2                       1010 -> -2
0011 -> +3                       1011 -> -3
0100 -> +4                       1100 -> -4
0101 -> +5                       1101 -> -5
0110 -> +6                       1110 -> -6
0111 -> +7                       1111 -> -7
```

Questa rappresentazione comporta, tuttavia, due seri problemi che la rendono inutilizza nella pratica:

* in primo luogo vi sono due rappresentazioni del numero zero (ossia 0000 e 1000) il che non è opportuno;
* il problema maggiore è tuttavia il fatto che con tale rappresentazione non è possibile continuare ad utilizzare le regole usuali per fare le operazioni aritmetiche (nell'aritmetica decimale tradizionale siamo abituati ad utilizzare esattamente le stesse regole di calcolo sia nel caso dei numeri positivi sia nel caso di quelli negativi). Ad esempio:

```
 0010 + (+2)
 1011 = (-3)
 ---------
 1101   (-5 ERRATO)
```

E' opportuno, quindi, scegliere una rappresentazione che elimini tali due problemi. La scelta è di usare una rappresentazione che prende il nome di "**complemento a due**" e che definiamo dalle seguenti regole:

* Il bit più significativo (a sinistra) rappresenta il segno del numero; 0 per i numeri positivi; 1 per i numeri negativi.
* La rappresentazione di un numero positivo si ottiene semplicemente codificando con i bit restanti il valore assoluto del numero (ossia il numero senza segno). In altre parole, se usa N bit, gli N-1 bit esclusi il primo sono la codifica binaria valore assoluto del numero. Ad esempio la codifica binaria di +5 si ottiene con uno 0 iniziale seguito dalla codifica binaria di 5, ossia: 0101
* La rappresentazione di un numero negativo si ottiene in tre passi:
  * si considera il numero positivo con lo stesso valore assoluto (ad esempio, per rappresentare -5, si considera +5) e la sua rappresentazione (nell'esempio 0101);
  * si invertono tutti i bit in tale rappresentazione (ossia si mette un 1 dove c'è 0 e viceversa: operazione di complemento a 1); nell'esempio si ottiene 1010;
  * si somma 1 al risultati ottenuto al passo precedente, ossia: 1010 + 1 = 1011

Il vantaggio di tale rappresentazione è il fatto che si possono effettuare tutte le operazioni aritmetiche usando i metodi usuali. In primo luogo, utilizzando 4 bit si possono codificare i numeri da `-2^(4-1)` a `2^(4 -1) - 1`, ossia da -8 a 7:

```
0000 -> +0                       1000 -> -8
0001 -> +1                       1001 -> -7
0010 -> +2                       1010 -> -6
0011 -> +3                       1011 -> -5
0100 -> +4                       1100 -> -4
0101 -> +5                       1101 -> -3
0110 -> +6                       1110 -> -2
0111 -> +7                       1111 -> -1
```

Si hanno allora i seguenti esempi di operazioni:

```

  0010 + (+2)
  1011 = (-5)
------------
  1101   (-3)
  
  0111 + (+7)
  1011 = (-5)
------------
  0010   (+2) Overflow ignorato
```

Per effettuare la conversione inversa, ossia, data la rappresentazione in complemento a due, ottenere il numero con segno, si procede come segue:

* Se il primo bit è 0 il numero è positivo. Si esegue allora la conversione da binario a decimale dei bit successivi e si ottiene il valore del numero; ad esempio, dato 0111, si converte 111 ottenendo il valore 7 e si ha quindi il numero decimale con segno +7;
* Se il primo bit è 1 il numero è negativo. Per ottenere il valore assoluto del numero si considerano tutti i bit tranne il primo, si invertono e si esegue la conversione da binario a decimale della rappresentazione così ottenuta; si deve quindi aggiungere 1 per ottenere il valore assoluto del numero decimale con segno. Ad esempio, dato 1011, si esclude il primo bit e si considerano i restanti (011); si invertono tali bit ottenendo 100 che è la codifica di 4 cui va aggiunto 1 ottenendo il valore assoluto 5 del numero con segno che risulta quindi essere -5.

E' importante notare che il complemento a due è la più diffusa codifica di numeri con segno ma non l'unica. Un'altra rappresentazione utilizzata è il "complemento a uno" concettualmente simile e che non analizzeremo.

#### &#x20;N-bit con rappresentazione del segno

![](https://875337804-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lt58INaXg-cFL27pnx0%2F-LuH0G05EiWcbv87IKYE%2F-LuH0vRUXQqUwsP1XO0r%2FDataRep_SignMagnitude.png?alt=media\&token=ea50bd1c-55f0-4a4d-9415-e257014df3eb)

#### Rappresentazione in complemento a 1

Complemento a 1: si invertono 0 con 1 e viceversa.

![](https://875337804-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lt58INaXg-cFL27pnx0%2F-LuH0G05EiWcbv87IKYE%2F-LuH100IoAs9uiHlH6Es%2FDataRep_OneComplement.png?alt=media\&token=aa88c04d-5d31-472a-b338-2d4c9ea315b2)

#### Rappresentazione in complemento a 2

Quella poi utilizzata degli elaboratori per eseguire operazioni con i numeri negativi: come sono rappresentati i numeri negativi in memoria.

![](https://875337804-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lt58INaXg-cFL27pnx0%2F-LzRJnNHCmQv_gvvuHbk%2F-LzXUtbodhM5B6pS5-S9%2FDataRep_TwoComplement%20\(1\).png?alt=media\&token=7c824565-90e4-405b-942c-952840e74931)

### **Computers use 2's Complement Representation for Signed Integers**

We have discussed three representations for signed integers: signed-magnitude, 1's complement and 2's complement. Computers use 2's complement in representing signed integers. This is because:

1. There is only one representation for the number zero in 2's complement, instead of two representations in sign-magnitude and 1's complement.
2. Positive and negative integers can be treated together in addition and subtraction. Subtraction can be carried out using the "addition logic".

**Example 1**: Addition of Two Positive Integers: Suppose that `n=8, 65D + 5D = 70D`

```
65D →    0100 0001B
 5D →    0000 0101B +
         -----------
         0100 0110B    → 70D (OK)
```

**Example 2**: Subtraction is treated as Addition of a Positive and a Negative Integers: Suppose that `n=8, 65D - 5D = 65D + (-5D) = 60D`

```
65D →    0100 0001B
-5D →    1111 1011B +
         -----------
         0011 1100B    → 60D (discard carry - OK)
```

**Example 3**: Addition of Two Negative Integers: Suppose that `n=8, -65D - 5D = (-65D) + (-5D) = -70D`

```
-65D →    1011 1111B
 -5D →    1111 1011B +
          ------------
          1011 1010B    → -70D (discard carry - OK)
```

Because of the _fixed precision_ (i.e., _fixed number of bits_), an _n_-bit 2's complement signed integer has a certain range. For example, for _`n`_`=8`, the range of 2's complement signed integers is `-128` to `+127`. During addition (and subtraction), it is important to check whether the result exceeds this range, in other words, whether _overflow_ or _underflow_ has occurred.

**Example 4**: Overflow: Suppose that `n=8, 127D + 2D = 129D` (overflow - beyond the range)

```
127D →    0111 1111B
  2D →    0000 0010B +
          ------------
          1000 0001B    → -127D (wrong)
```

**Example 5**: Underflow: Suppose that `n=8, -125D - 5D = -130D` (underflow - below the range)

```
-125D →    1000 0011B
  -5D →    1111 1011B +
           ------------
           0111 1110B    → +126D (wrong)
```

The following diagram explains how the 2's complement works. By re-arranging the number line, values from `-128` to `+127` are represented contiguously by ignoring the carry bit.

### RANGE VALORI CON RAPPRESENTAZIONE INTERI IN COMPLEMENTO A 2

| N° bit usati | VALORE MINIMO                                | VALORE MASSIMO                                                    |
| ------------ | -------------------------------------------- | ----------------------------------------------------------------- |
| 8            |  `- (2^7)`   (= -128)                        |  `+ (2^7)  - 1`      (= +127)                                     |
| 16           |  `- (2^15)`  (= -32.768)                     |  `+ (2^15) - 1`    (= +32.767)                                    |
| 32           |  `- (2^31)`  (= -2.147.483.648)              |  `+ (2^31) - 1`    (= +2.147.483.647) (9+ digits)                 |
| 64           |  `- (2^63)`   (= -9.223.372.036.854.775.808) |  `+ (2^63)  -1`       (= +9.223.372.036.854.775.807) (18+ digits) |

### RAPPRESENTAZIONE FLOATING POINT (VIRGOLA MOBILE)

I numeri a virgola mobile (o reali) possono rappresentare numeri di valore molto grande, come  (`1.23×10^88`) o molto piccoli (`1.23×10^-88`) . Esso può rappresentare numeri negativi molto grandi   (`-1.23×10^88`) o numeri negativi molto piccoli   (`-1.23×10^-88`)così come lo `0`, come illustrato nella figura:

![](https://875337804-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lt58INaXg-cFL27pnx0%2F-LuRhQAHLhy8Ue4yUKrm%2F-LuRjjQYu-M8BnCN5w6R%2FRepresentation_FloatingPointNumbers.png?alt=media\&token=1aa3f5f1-f2f4-43ca-a146-3b464c9f6e11)

Un numero di tipo, floating point, è tipicamente espresso in notazione scientifica, con una parte frazionaria (F), detta anche _mantissa_, e un esponente (E) e una radice, detta anche base (o radice), (r), nella forma `F * r^E`. I numeri decimali usano una radice di 10 (F \* 10^E); mentre i numeri binari utilizzano una radice di 2 (F \* 2^E).

La rappresentazione di un numero di tipo floating point, non è univoca. Per esempio il numero 55.66 può essere rappresentato come 5.566 \* 10 ^ 1 o 0.5566 \* 10^2 oppure 0.05566 \* 10^3, e così via. Così la parte frazionaria può essere _normalizzata_. Nella forma normalizzata, c'è una sola cifra che non sia zero prima del punto.  Per esempio il numero decimale 123.456 può essere normalizzato come 1.23456 \* 10^2; il numero binario 1010.1011 può essere normalizzato come 1.0101011 \* 2^3.

E' importante notare che i numero di tipo float soffrono della **perdita di precisione** quando rappresentati con un numero fisso di bit (es 32 o 64 bit). Questo è dovuto al fatto che ci sono infiniti numeri reali (anche in un intervallo tra 0.0 e 0.1). D'altro canto, utilizzando n bit, possiamo avere solo 2^n numeri distinti. Quindi non tutti i numeri reali possono essere rappresentati. La più vicina approssimazione sarà invece usata, risultando così in una perdita di accuratezza.

E' anche importante notare che l'aritmetica con i numeri floating point è molto meno efficiente di quella   con i numeri interi. Può essere velocizzata con i coprocessori dedicati floating point. Quindi utilizzeremo gli interi se la nostra applicazione non necessita numeri floating point.

Nei computer, i numeri floating point sono rappresentati in notazione scientifica come frazione (F), esponente (E) e radice 2, nella forma `F*2^E`. Entrambi E ed F possono essere positivi come negativi. I computer moderni adottano lo standard IEEE 754 per rappresentare i numeri floating point. Ci sono due differenti schemi di rappresentazione: 32 bit a precisione singola e 64 bit a precisione doppia.

#### **IEEE-754 32-bit Single-Precision Floating-Point Numbers**

Nella rappresentazione a 32 bit a precisione singola:

* il bit più significativo è il _bit di segno_ (S), con 0 per i numeri positivi e 1 per quelli negativi;
* i seguenti 8 bit rappresentano l'_esponente_ (E);
* i rimanenti 23 bit, rappresentano la frazione (F);&#x20;



![](https://875337804-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lt58INaXg-cFL27pnx0%2F-LvLgvec4hygUPXBxIFb%2F-LvLkQZZOHe1Dr3UuQDR%2FDataRep_Float.png?alt=media\&token=82323eba-442d-4c9a-b99b-90576f508fb7)

**La forma normalizzata**

Illustriamo con un esempio, supponiamo che il pattern dei 32 bit, sia  `1 1000 0001 011 0000 0000 0000 0000 0000`, con:

* S = 1
* E = 1000 0001
* F = 011 0000 0000 0000 0000 0000

Nella forma normalizzata, la frazione effettiva è normalizzata con un 1 in testa, quindi come `1.F`. In questo esempio, la frazione effettiva è `1.011 0000 0000 0000 0000 0000` =  1 + 1×2^-2 + 1×2^-3 = 1.375D.&#x20;

Il bit di segno, rappresenta il segno del numero, con S=0 per i positivi e S=1 per i numeri negativi. Questo è un numero negativo, -1.375D.

Nella forma normalizzata, l'esponente effettivo è E - 127 (così chiamato excess - 127). Questo perché abbiamo bisogno di rappresentare sia gli esponenti negativi che positivi. Con 8 bit, nel range quindi da 0 a 255, con lo schema excess - 127 possiamo provvedere esponenti da -127 a 128. In questo esempio, l'esponente effettivo è: E - 127 = 129 - 127 = 2D.

&#x20;Quindi il numero rappresentato è `-1.375×2^2=-5.5D`.

**La forma denormalizzata**

La forma normalizzata ha un problema serio, con un 1 implicito in testa alla frazione, non è possibile rappresentare lo 0!.&#x20;

La forma _denormalizzata_ è utilizzata per rappresentare lo 0 e altri numeri.

Per E=0, i numeri sono nella forma _denormalizzata_. Uno 0 implicito (invece dell'1) è inserito alla testa  della frazione; e l'esponente effettivo è sempre -126. Quindi il numero zero può essere rappresentato con E=0 e F=0 (perché `0.0×2^-126=0` ).

In questo modo è possibile rappresentare numeri molto piccoli, negativi o positivi nella forma _denormalizzata_ con E = 0. Per esempio, se S=1, E=0, e F=`011 0000 0000 0000 0000 0000`. La frazione effettivo è `0.011=1×2^-2+1×2^-3=0.375D`. Poiché S=1, esso è un numero negativo. Con E=0, l'esponente effettivo è `-0.375×2^-126 = -4.4×10^-39`, che è un numero negativo molto piccolo (vicino allo 0).&#x20;

**Sommario**

Riassumendo, il valore (N) è calcolato nel modo seguente:

* per 1 ≤ E ≤ 254, `N = (-1)^S × 1.F × 2^(E-127)`. Questi numeri sono la così detta forma _normalizzata_. Il sign-bit rappresenta il segno  del numero. La parte frazionaria (1.F) è normalizzata con un 1 in testa implicito. L'esponente è in eccesso di 127, così in modo da rappresentare sia esponenti positivi che negativi. Il range degli esponenti è -126 a +127.
* per E = 0, `N = (-1)^S × 0.F × 2^(-126)`. Questi numeri sono la cosiddetta forma _denormalizzata_. L'esponente di 2^-126 genera numeri molto piccoli. La forma _denormalizzata_ è necessaria per rappresentare lo zero (con F=0 e E=0). Esso può rappresentare anche numeri molto piccoli vicini allo 0, sia positivi che negativi.
* per E = 255, esso rappresenta valori speciali come ±INF (infinito positivo o negativo) e  `NaN` (not a number).&#x20;

**Example 1**: Suppose that IEEE-754 32-bit floating-point representation pattern is `0 10000000 110 0000 0000 0000 0000 0000`.

```
Sign bit S = 0 ⇒ positive number
E = 1000 0000B = 128D (in normalized form)
Fraction is 1.11B (with an implicit leading 1) = 1 + 1×2^-1 + 1×2^-2 = 1.75D
The number is +1.75 × 2^(128-127) = +3.5D
```

&#x20;**Example 2:** Suppose that IEEE-754 32-bit floating-point representation pattern is `1 01111110 100 0000 0000 0000 0000 0000`.

```
Sign bit S = 1 ⇒ negative number
E = 0111 1110B = 126D (in normalized form)
Fraction is 1.1B  (with an implicit leading 1) = 1 + 2^-1 = 1.5D
The number is -1.5 × 2^(126-127) = -0.75D
```

&#x20;**Example 3:** Suppose that IEEE-754 32-bit floating-point representation pattern is `1 01111110 000 0000 0000 0000 0000 0001`.

```
Sign bit S = 1 ⇒ negative number
E = 0111 1110B = 126D (in normalized form)
Fraction is 1.000 0000 0000 0000 0000 0001B  (with an implicit leading 1) = 1 + 2^-23
The number is -(1 + 2^-23) × 2^(126-127) = -0.500000059604644775390625 (may not be exact in decimal!)
```

&#x20;**Example 4** (**De-Normalized Form**): Suppose that IEEE-754 32-bit floating-point representation pattern is `1 00000000 000 0000 0000 0000 0000 0001`.

```
Sign bit S = 1 ⇒ negative number
E = 0 (in de-normalized form)
Fraction is 0.000 0000 0000 0000 0000 0001B  (with an implicit leading 0) = 1×2^-23
The number is -2^-23 × 2^(-126) = -2×(-149) ≈ -1.4×10^-45
```

#### **IEEE-754 64-bit Double-Precision Floating-Point Numbers**

Lo schema di rappresentazione per i 64-bit a doppia precisione è simile a quello a 32-bit a singola precisione:

* Il bit più significativo è il bit di segno (S), con 0, per i numeri positivi e 1 per i numeri negativi;
* I seguenti 11 bit rappresentano l'esponente (E);
* I rimanenti 52 bit rappresentano la frazione (F);

![](https://875337804-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lt58INaXg-cFL27pnx0%2F-LvLgvec4hygUPXBxIFb%2F-LvLkV8x6Pknh4wI_N5A%2FDataRep_Double.png?alt=media\&token=4a14ed04-1792-44d7-ae25-2d821af2fdcc)

Il valore (N) è calcolato come segue:

* La forma _normalizzata_: con  `1 ≤ E ≤ 2046`, `N = (-1)^S × 1.F × 2^(E-1023)`.
* La forma _denormalizzata_: con `E = 0`, `N = (-1)^S × 0.F × 2^(-1022)`.
* Per `E = 2047`, N rappresenta valori speciali, come  `±INF` (infinito), `NaN` (not a number).

### Approfondimento sulla rappresentazione Floating-Point

Ci sono tre parti nella rappresentazione floating-point

* Il bit di segno (S) si autoesplica (0 per i positivi e 1 per i negativi);
* Per l'esponente (E) un bias o eccesso è applicato così da rappresentare sia i positivi che i negativi. Il bias è settato a metà del range. Per la precisione singola con esponente a 8 bit, il bias è 127 (o eccesso -127). Per la doppia precisione con un esponente a 11 bit, il bias è 1023 (o eccesso -1023);
* fa frazione F (anche chiamata _mantissa_ o _significand_) è composta da un bit in testa implicito (prima del punto radice) e i bit frazionari (dopo il punto radice). Il bit di testa per i numeri normalizzati è 1; mentre il bit di testa per i numeri denormalizzati è 0.

#### Numeri floating-point normalizzati

Nella forma normalizzata, il punto radice è situato dopo la prima cifra diversa da zero, es:   `9.8765D×10^-23D`, `1.001011B×2^11B`. Per i numeri binari, il primo bit è sempre 1, e non viene rappresentato esplicitamente, in modo da non sprecare un bit di memoria.

Nella forma normalizzata IEEE 754:

*   per la precisione singola, 1 ≤ E ≤ 254 con eccesso di 127. Quindi l'esponente effettivo è da -126 a +127. Gli esponenti negativi sono utilizzati per esprimere numeri piccoli (<1.0); mentre esponenti positivi sono utilizzati per rappresentare numeri grandi (>1.0).

    &#x20; `N = (-1)^S × 1.F × 2^(E-127)`
*   per la precisione doppia, 1 ≤ E ≤ 2046 con eccesso di 1023. L'esponente effettivo è da -1022 a +1023, e

    &#x20; `N = (-1)^S × 1.F × 2^(E-1023)`

Consideriamo che una sequenza di n-bit ha un numero _finito_ di combinazioni (=2^n), che possono rappresentare un numero finito di diversi numeri. E' impossibile rappresentare gli infiniti numeri  sull'asse reale (anche in un piccolo intervallo tra 0.0 e 0.1 ci sono infiniti numeri). Cioè non tutti i numeri floating-point possono essere rappresentati accuratamente. Invece, la più vicina approssimazione è usata, questo causa una _perdita di accuratezza_.

Minimi e massimi della forma normalizzata:

* A singola precisione

| Normalizzato (MIN)                                                                                                                           | Normalizzato (MAX)                                                                                                                                                                           |
| -------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <p>0080 0000H </p><p>0 00000001 00000000000000000000000B </p><p>E = 1, F = 0 </p><p>N(min) = 1.0B × 2^-126 </p><p>(≈1.17549435 × 10^-38)</p> | <p>7F7F FFFFH </p><p>0 11111110 11111111111111111111111B </p><p>E = 254, F = 11111111111111111111111B </p><p>N(max) = 1.1...1B × 2^127 = (2 - 2^-23) × 2^127 </p><p>(≈3.4028235 × 10^38)</p> |

* A doppia precisione

| Normalizzato (MIN)                                                                               | Normalizzato (MAX)                                                                                                      |
| ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| <p>0010 0000 0000 0000H</p><p>N(min) = 1.0B × 2^-1022 </p><p>(≈2.2250738585072014 × 10^-308)</p> | <p>7FEF FFFF FFFF FFFFH</p><p>N(max) = 1.1...1B × 2^1023 = (2 - 2^-52) × 2^1023</p><p>≈1.7976931348623157 × 10^308)</p> |

![](https://875337804-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lt58INaXg-cFL27pnx0%2F-MROtSgQI3o02YDsUb0X%2F-MRPEQBYBiMIEUp1VoBG%2FDataRep_RealNumbers.png?alt=media\&token=492ce44c-472a-462e-a763-16baecdfa5d7)

####

#### Numeri floating-point denormalizzati

Se E = 0, ma la frazione non è zero, allora il valore è nella forma **denormalizzata**, è un bit in testa a 0 è considerato, come segue:

*   Per la precisione singola, E = 0,

    `N = (-1)^S × 0.F × 2^(-126)`
*   Per la precisione doppia, E = 0,

    `N = (-1)^S × 0.F × 2^(-1022)`

La forma _denormalizzata_ può rappresentare numeri molto piccoli vicini allo zero e lo zero che non può essere rappresentato nella forma normalizzata, come mostrato nella figura sopra.

Il minimo e il massimo dei numeri _floating point in forma denormalizzata_ sono:

* A singola precisione

| Denormalizzato (MIN)                                                                                                                                                                          | Denormalizzato (MAX)                                                                                                                                                                   |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <p>0000 0001H </p><p>0 00000000 00000000000000000000001B </p><p>E = 0, F = 00000000000000000000001B </p><p>D(min) = 0.0...1 × 2^-126 = 1 × 2^-23 × 2^-126 = 2^-149 </p><p>(≈1.4 × 10^-45)</p> | <p>007F FFFFH</p><p>0 00000000 11111111111111111111111B</p><p>E = 0, F = 11111111111111111111111B</p><p>D(max) = 0.1...1 × 2^-126 = (1 -2^-23)×2^-126 </p><p>(≈1.1754942 × 10^-38)</p> |

* A doppia precisione

| Denormalizzato (MIN)                                                                                                 | Denormalizzato (MAX)                                                                                             |
| -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| <p>0000 0000 0000 0001H</p><p>D(min) = 0.0...1 × 2^-1022 = 1 × 2^-52 × 2^-1022 = 2^-1074 </p><p>(≈4.9 × 10^-324)</p> | <p>000F FFFF FFFF FFFFH</p><p>D(max) = 0.1...1 × 2^-1022 = (1-2^-52)×2^-1022 (≈4.4501477170144023 × 10^-308)</p> |

#### Valori speciali

**Zero:** il valore zero non può essere rappresentato nella forma normalizzata, deve essere rappresentato nella forma denormalizzata con E=0 e F=0. Ci sono due rappresentazioni dello zero: +0 con S=0 e -0 con S=1.

**Infinito:** il valore +infinito (es: 1/0) e -infinito ( -1/0), sono rappresentati con esponenti di tutti 1 (E = 255 per la precisione singola e E = 2047 per la precisione doppia), F=0, e S=0 per +INF e S=1 per -INF.

**Not a Number (NaN):** NaN  denota un valore che non può essere rappresentato come un numero reale (es: 0/0). NaN è rappresentato con E di tutti 1 (E = 255 per la precisione singola e E = 2047 per la doppia precisione) e qualsiasi valore diverso da 0 per la parte frazionaria.

### I numeri in virgola mobile nei linguaggi di programmazione

#### In linguaggio macchina

* Istruzioni specifiche per i calcoli in virgola mobile
* Spesso nei processori a 32 bit sono presenti istruzioni per il calcolo in doppia precisione (64 bit): si utilizzano due registri per operando!

Esempio:

```
add.s $f1, $f3, $f4      # $f1=$f3+$f4 (singola precisione-32 bit)
add.f $f0, $f2, $f4      # [$f0-$f1]=[$f2-$f3]+[$f4-$f5]
                         # (doppia precisione-64bit)
```

#### In linguaggio C

Sono disponibili i tipi _float_, _double_, _long double_ (cfr LINGUAGGIO C – Tipi predefiniti)

### IEEE-754 Floating Point Converter

[https://www.h-schmidt.net/FloatConverter/IEEE754.html](https://www.h-schmidt.net/FloatConverter/IEEE754.html)&#x20;

Questa pagina permette di convertire tra la rappresentazione decimale dei numeri (come "1.02") e il formato binario utilizzato da tutte le moderne CPU (IEEE 754 floating point).

{% embed url="https://binary-system.base-conversion.ro/convert-real-numbers-from-decimal-system-to-32bit-single-precision-IEEE754-binary-floating-point.php" %}

### APPROFONDIMENTI

{% embed url="https://github.com/checksound/CodificaNumeri" %}
