Basi 3 - numeri reali

Numeri reali

Python salva i numeri reali (numeri in virgola mobile) in 64 bit di informazione divisi in segno, esponente e mantissa. Vediamo un esempio:

[2]:
3.14
[2]:
3.14
[3]:
type(3.14)
[3]:
float

ATTENZIONE: bisogna usare il punto invece della virgola!

Quindi scriverai 3.14 invece di 3,14

Fai molta attenzione quando copi numeri da documenti in italiano, potrebbero contenere insidiose virgole !

Notazione scientifica

Quando i numeri sono molto grandi o molto piccoli, per evitare di scrivere troppi zero conviene usare la notazione scientitifica con la e come \(xen\), che moltiplica il numero x per \(10^n\)

Con questa notazione, in memoria vengono messe solo le cifre più significative ( la mantissa ) e l’esponente, evitando quindi di sprecare spazio.

[4]:
75e1
[4]:
750.0
[5]:
75e2
[5]:
7500.0
[6]:
75e3
[6]:
75000.0
[7]:
75e123
[7]:
7.5e+124
[8]:
75e0
[8]:
75.0
[9]:
75e-1
[9]:
7.5
[10]:
75e-2
[10]:
0.75
[11]:
75e-123
[11]:
7.5e-122

DOMANDA: Guarda le seguenti espressioni, e cerca di indovinare qule risultato producono (o se danno errore):

  1. print(1.000.000)
    
  2. print(3,000,000.000)
    
  3. print(2000000.000)
    
  4. print(2000000.0)
    
  5. print(0.000.123)
    
  6. print(0.123)
    
  7. print(0.-123)
    
  8. print(3e0)
    
  9. print(3.0e0)
    
  10. print(7e-1)
    
  11. print(3.0e2)
    
  12. print(3.0e-2)
    
  13. print(3.0-e2)
    
  14. print(4e2-4e1)
    

Numeri troppo grandi o troppo piccoli

A volte i calcoli su numeri estremamente piccoli o enormi possono produrre come risultato math.nan (Not a Number) o math.inf. Per il momento li menzioniamo e basta, trovi una discussione dettagliata nel foglio su Numpy

Esercizio - cerchio

Calcola l’area del cerchio al centro di un pallone da calcio (raggio = 9.15m), ricordandoti che \(area= pi*r^2\) (come operatore per la potenza, usa **):

Il tuo codice dovrebbe stampare come risultato 263.02199094102605

Mostra soluzione
[12]:

263.02199094102605

Nota che le parentesi intorno al r**2 non sono necessarie perchè l’operatore ** ha la precedenza, ma possono aiutare la leggibilità del codice.

Ricordiamo qua la precedenza degli operatori:

Operatore

Descrizione

**

Potenza (massima precedenza)

+ -

Più e meno unari

* / // %

Moltiplicazione, divisione, divisione intera, modulo

+ -

Addizione e sottrazione

<= < > >=

Operatori di comparazione

== !=

Operatori di uguaglianza

not or and

Operatori logici (minima precedenza)

Esercizio - frazionamento

Scrivi del codice per calcolare il valore della seguente formula per x = 0.000003, dovresti ottenere 2.753278226511882

\[\large-\frac{\sqrt{x+3}}{\frac{(x + 2)^3}{\log{x}}}\]
Mostra soluzione
[13]:
x = 0.000003

# scrivi qui


[13]:
2.753278226511882

Esercizio - sommatoria

Scrivi del codice per calcolare il valore della seguente espressione (non usare cicli, scrivi per esteso tutto i calcoli), dovresti ottenere 20.53333333333333

\[\large\sum_{j=1}^{3}{\frac{j^4}{j + 2}}\]
Mostra soluzione
[14]:
# scrivi qui


[14]:
20.53333333333333

Reali - conversione

Se vogliamo convertire un reale ad un intero, abbiamo a disposizione diversi modi:

Funzione

Descrizione

Simbolo matematico

Risultato

math.floor(x)

arrotonda x all’intero inferiore

\[\lfloor{8.7}\rfloor\]

8

int(x)

arrotonda x all’intero inferiore

\[\lfloor{8.7}\rfloor\]

8

math.ceil(x)

arrotonda x all’intero superiore

\[\lceil{5.3}\rceil\]

6

round(x)

arrotonda x all’intero più vicino

\[\lfloor{2.49}\rceil\]

2

\[\lfloor{2.51}\rceil\]

3

DOMANDA: Guarda le espressioni seguenti, e per ciascuna cerca di indovinare quale risultato produce (o se da errore).

  1. math.floor(2.3)
    
  2. math.floor(-2.3)
    
  3. round(3.49)
    
  4. round(3.51)
    
  5. round(-3.49)
    
  6. round(-3.51)
    
  7. math.ceil(8.1)
    
  8. math.ceil(-8.1)
    

DOMANDA: Dato un qualsiasi float x, la seguente formula è :

math.floor(math.ceil(x)) == math.ceil(math.floor(x))
  1. sempre True

  2. sempre False

  3. a volte True e a volte False (fornire esempi)

Mostra risposta

DOMANDA: Dato un qualsiasi float x, la seguente formula è :

math.floor(x) == -math.ceil(-x)
  1. sempre True

  2. sempre False

  3. a volte True e a volte False (fornire esempi)

Mostra risposta

Esercizio - tonificante

Gli studi eccessivi ti hanno portato a cercare su internet ricette di bevande energetiche. Fortunatamente, una guru della nutrizione ha appena postato sul suo canale Instagram @BeviCheTiFaBene la ricetta di un frappè portentoso:

Versare in un contenitore 2 decilitri di spremuta di kiwi, 4 decilitri di salsa di soia, e 3 decilitri di shampoo al karitè bio. Miscelare vigorosamente e poi versare metà del composto in un bicchiere. Rabboccare il bicchiere fino al decilitro intero superiore. Tracannare avidamente.

Corri a comprare gli ingredienti, e ti appresti a miscelarli. Hai a disposizione un misurino in cui travasi i preziosi fluidi, ad uno ad uno. Nell’effettuare il travaso, versi sempre un po’ più del necessario (ma mai più di 1 decilitro), e ad ogni ingrediente elimini poi l’eccesso.

  • NON usare sottrazioni, prova ad usare solo gli operatori per gli arrotondamenti

Esempio:

Dati

kiwi = 2.4
soia = 4.8
shampoo = 3.1
misurino = 0.0
mixer = 0
bicchiere = 0.0

Il tuo codice deve stampare:

Verso nel misurino 2.4 dl di kiwi, tolgo eccesso fino a tenere 2 dl
Travaso nel mixer, adesso contiene 2 dl
Verso nel misurino 4.8 dl di soia, tolgo eccesso fino a tenere 4 dl
Travaso nel mixer, adesso contiene 6 dl
Verso nel misurino 3.1 dl di shampoo, tolgo eccesso fino a tenere 3 dl
Travaso nel mixer, adesso contiene 9 dl
Verso metà della miscela ( 4.5 dl ) nel bicchiere
Rabbocco il bicchiere fino al decilitro intero superiore, adesso contiene 5 dl
Mostra soluzione
[15]:
kiwi = 2.4
soia = 4.8
shampoo = 3.1
misurino = 0.0
mixer = 0.0
bicchiere = 0.0


# scrivi qui


Verso nel misurino 2.4 dl di kiwi, tolgo eccesso fino a tenere 2 dl
Travaso nel mixer, adesso contiene 2.0 dl
Verso nel misurino 4.8 dl di soia, tolgo eccesso fino a tenere 4 dl
Travaso nel mixer, adesso contiene 6.0 dl
Verso nel misurino 3.1 dl di shampoo, tolgo eccesso fino a tenere 3 dl
Travaso nel mixer, adesso contiene 9.0 dl
Verso metà della miscela ( 4.5 dl ) nel bicchiere
Rabbocco il bicchiere fino al decilitro intero superiore, adesso contiene: 5 dl

Esercizio - arrotondamente

Scrivi del codice per calcolare il valore della seguente formula per x = -5.51, dovresti ottenere 41

\[\large\lvert{\lceil{x}\rceil}\rvert + \lfloor{x}\rceil^2\]
Mostra soluzione
[16]:
import math

x = -5.51   # 41
#x = -5.49  # 30

# scrivi qui


[16]:
41

Reali - uguaglianza

ATTENZIONE: Ciò che segue vale per *tutti* i linguaggi di programmazione!

Alcuni risultati ti parranno curiosi ma è il modo in cui la maggior parte dei processori (CPU) opera, indipendentemente da Python.

Quando si effettuano calcoli con i numeri in virgola mobile, il processore può commettere degli errori di arrotondamento dovuto ai limiti della rappresentazione interna. Sotto il cofano i numeri float vengono memorizzati in una sequenza in codice binario di 64 bit, secondo lo standard IEEE-754 floating point arithmetic : questo impone un limite fisico alla precisione dei numeri, e a volte possono anche capitare sorpese dovute alla conversione da decimale a binario. Per esempio, proviamo a stampare 4.1:

[17]:
print(4.1)
4.1

Per nostra convenienza Python ci mostra 4.1, ma in realtà nella memoria del processore è finito un numero diverso ! Quale? Per scoprire cosa nasconde, con format possiamo esplicitamente formattare il numero con lo specificatore di formato f usando per esempio 55 cifre di precisione:

[18]:
format(4.1, '.55f')
[18]:
'4.0999999999999996447286321199499070644378662109375000000'

Possiamo quindi chiederci quale potrà essere il risultato di un calcolo:

[19]:
print(7.9 - 3.8)
4.1000000000000005

Notiamo che il risultato è ancora diverso da quello atteso ! Indagando meglio, notiamo che Python non ci sta nemmeno mostrando tutte le cifre:

[20]:
format(7.9 - 3.8, '.55f')
[20]:
'4.1000000000000005329070518200751394033432006835937500000'

E se volessimo sapere se due calcoli coi float producono lo ‘stesso’ risultato?

ATTENZIONE: EVITA IL == CON I FLOAT!

Per capire se il risultato tra due calcoli con i float è uguale, NON puoi usare l’operatore ==:

[21]:
7.9 - 3.8 == 4.1    # GUAI IN VISTA !
[21]:
False

Invece, devi preferire alternative che valutano se un numero float è vicino ad un altro, come per esempio la comoda funzione math.isclose:

[22]:
import math

math.isclose(7.9 - 3.8, 4.1)   # MOLTO MEGLIO
[22]:
True

Di default math.isclose usa una precisione di 1e-09 ma volendo, a math.isclose puoi anche passare un limite di tolleranza entro la quale deve essere la differenza tra i due numeri affinchè siano considerati uguali:

[23]:
math.isclose(7.9 - 3.8, 4.1, abs_tol=0.000001)
[23]:
True

DOMANDA: Possiamo rappresentare perfettamente il numero \(\sqrt{2}\) in un float?

Mostra risposta

DOMANDA: Quali di queste tre espressioni di codice danno lo stesso risultato?

import math
print('a)', math.sqrt(3)**2 == 3.0)
print('b)', abs(math.sqrt(3)**2 - 3.0) < 0.0000001)
print('c)', math.isclose(math.sqrt(3)**2, 3.0, abs_tol=0.0000001))
Mostra risposta

Esercizio - quadratica

Scrivi del codice che calcola gli zero dell’equazione \(ax^2-b = 0\)

  • Mostra i numeri con 20 cifre di precisione

  • Alla fine controlla che sostituendo il valore ottenuto di x nell’equazione si ottenga effettivamente zero.

Esempio - dati:

a = 11.0
b = 3.3

dopo il tuo codice deve stampare:

11.0 * x**2 - 3.3 = 0  per x1 = 0.54772255750516607442
11.0 * x**2 - 3.3 = 0  per x2 = -0.54772255750516607442
0.54772255750516607442 è una soluzione? True
-0.54772255750516607442 è una soluzione? True
Mostra soluzione
[24]:
a = 11.0
b = 3.3

# scrivi qui


11.0 * x**2 - 3.3 = 0  per x1 = 0.54772255750516607442
11.0 * x**2 - 3.3 = 0  per x2 = -0.54772255750516607442
0.54772255750516607442 è una soluzione? True
-0.54772255750516607442 è una soluzione? True

Esercizio - alla moda

Stai già pensando alle prossime vacanze, ma c’è un problema: dove vai, se un selfie-stick non ce l’hai? Non puoi partire con questo grave turbamento: per uniformarti al fenomeno di massa devi comprare lo stick più simile agli altri. Conduci quindi prima un’indagine statistica tra turisti malati di selfie-stick con l’obiettivo di scoprire i brand più frequenti, in altre parole, la moda delle frequenze. Ottieni questi risultati:

[25]:
b1,b2,b3,b4,b5 = 'TroppiLike', 'ErMejo United', 'Perditempo SpA', 'Vanità 3.0','TronistiPerCaso' # brand
f1,f2,f3,f4,f5 = 0.25, 0.3, 0.1, 0.05, 0.3   # frequenze (in percentuale)

Desumiamo quindi che le masse prediligono i selfie-stick dei brand 'ErMejo United' e 'TronistiPerCaso', entrambi a parimerito con un 30% di turisti ciascuno. Scrivi quindi del codice che stampa questo risultato:

TroppiLike è il più frequente? False ( 25.0 % )
ErMejo United è il più frequente? True ( 30.0 % )
Perditempo Inc è il più frequente? False ( 10.0 % )
Vanità 3.0 è il più frequente? False ( 5.0 % )
TronistiPerCaso è il più frequente? True ( 30.0 % )
  • ATTENZIONE: il tuo codice deve funzionare con QUALUNQUE serie di variabili !!

Mostra soluzione
[26]:
b1,b2,b3,b4,b5 = 'TroppiLike', 'ErMejo United', 'Perditempo Inc', 'Vanità 3.0','TronistiPerCaso'    # brand
f1,f2,f3,f4,f5 = 0.25, 0.3, 0.1, 0.05, 0.3         # frequenze (in percentuale)  False True False False True
# OCCHIO, sembrano uguali ma deve funzionare anche con queste ;-)
#f1,f2,f3,f4,f5 = 0.25, 0.3, 0.1, 0.05, 0.1 + 0.2  #  False True False False True

# scrivi qui


TroppiLike è il più frequente? False ( 25.0 % )
ErMejo United è il più frequente? True ( 30.0 % )
Perditempo Inc è il più frequente? False ( 10.0 % )
Vanità 3.0 è il più frequente? False ( 5.0 % )
TronistiPerCaso è il più frequente? True ( 30.0 % )

Numeri decimali

Per moltissime applicazioni i float sono sufficienti, avendo coscienza dei loro limiti di rappresentazione e uguaglianza. Se proprio hai bisogno di maggiore precisione e/o prevedibilità, Python offre un tipo numerico apposito chiamato Decimal, che permette una precisione arbitraria. Per usarlo, devi prima importarlo dalla libreria decimal:

[27]:
from decimal import Decimal

Possiamo creare un Decimal a partire da una stringa:

[28]:
Decimal('4.1')
[28]:
Decimal('4.1')

ATTENZIONE: se crei un Decimal a partire da una costante, indicala come stringa!

Se gli passi un float rischi di perdere l’utilità dei Decimal:

[29]:
Decimal(4.1)  # così mi ritrovo coi problemi dei float ...
[29]:
Decimal('4.0999999999999996447286321199499070644378662109375')

Operazioni tra Decimal producono altri Decimal:

[30]:
Decimal('7.9') - Decimal('3.8')
[30]:
Decimal('4.1')

Questa volta, possiamo tranquillamente usare l’operatore di uguaglianza e ottenere il risultato atteso:

[31]:
Decimal('4.1') == Decimal('7.9') - Decimal('3.8')
[31]:
True

Sono anche supportate alcune funzioni matematiche, e spesso si comportano in modo più predicibile (nota che non stiamo usando math.sqrt):

[32]:
Decimal('2').sqrt()
[32]:
Decimal('1.414213562373095048801688724')

Ricordati comunque che la memoria del computer è finita!

I Decimal non possono essere la panacea per tutti i mali: per es, \(\sqrt{2}\) non ci starà mai completamente nella memoria di nessun computer ! Possiamo verificare le limitazioni elevando al quadrato il risultato:

[33]:
Decimal('2').sqrt()**Decimal('2')
[33]:
Decimal('1.999999999999999999999999999')

L’unica cosa che possiamo avere in più coi Decimal è un maggiore numero di cifre per rappresentarli, che volendo possiamo aumentare a piacere finchè non esauriamo la memoria del nostro pc. In questo libro non parleremo oltre dei Decimal perchè tipicamente servono solo per applicazioni specifiche, per esempio, se devi fare calcoli finanziari vorrai probabilmente usare cifre esatte !

Prosegui

Continua con le challenges