Quanto segue spiega come arrotondare i numeri in Python arrotondando o arrotondando ad un numero pari. Si presume che i numeri siano di tipo float in virgola mobile o int intero.
- funzione incorporata (per esempio nel linguaggio di programmazione):
round()
- Arrotonda i decimali a qualsiasi numero di cifre.
- Arrotonda i numeri interi a qualsiasi numero di cifre.
- round() arrotonda a un numero pari, non a un arrotondamento comune
- biblioteca standard
decimal
quantize()
Decimal
Creare un oggetto- Arrotondamento dei decimali a qualsiasi numero di cifre e arrotondamento ai numeri pari
- Arrotondamento dei numeri interi a qualsiasi numero di cifre e arrotondamento ai numeri pari
- Definire una nuova funzione
- Arrotonda i decimali a qualsiasi numero di cifre.
- Arrotonda i numeri interi a qualsiasi numero di cifre
- Nota: per valori negativi
Notate che, come detto sopra, la funzione built-in round non è un arrotondamento generale, ma un arrotondamento ad un numero pari. Vedi sotto per i dettagli.
funzione incorporata (per esempio nel linguaggio di programmazione): round()
Round() è fornita come funzione integrata. Può essere usata senza importare alcun modulo.
Il primo argomento è il numero originale e il secondo argomento è il numero di cifre (a quante cifre arrotondare).
Arrotonda i decimali a qualsiasi numero di cifre.
Quello che segue è un esempio di elaborazione per il tipo float a virgola mobile.
Se il secondo argomento viene omesso, viene arrotondato a un intero. Il tipo diventa anche un tipo int intero.
f = 123.456 print(round(f)) # 123 print(type(round(f))) # <class 'int'>
Se il secondo argomento è specificato, restituisce un tipo float in virgola mobile.
Se viene specificato un intero positivo, viene specificata la posizione decimale; se viene specificato un intero negativo, viene specificata la posizione intera. -1 arrotonda al decimo più vicino, -2 arrotonda al centesimo più vicino, e 0 arrotonda a un intero (il primo posto), ma restituisce un tipo float, diversamente da quando viene omesso.
print(round(f, 1)) # 123.5 print(round(f, 2)) # 123.46 print(round(f, -1)) # 120.0 print(round(f, -2)) # 100.0 print(round(f, 0)) # 123.0 print(type(round(f, 0))) # <class 'float'>
Arrotonda i numeri interi a qualsiasi numero di cifre.
Quello che segue è un esempio di elaborazione per il tipo integer int.
Se il secondo argomento viene omesso, o se viene specificato 0 o un intero positivo, il valore originale viene restituito così com'è. Se viene specificato un intero negativo, viene arrotondato alla cifra intera corrispondente. In entrambi i casi, viene restituito un intero di tipo int.
i = 99518 print(round(i)) # 99518 print(round(i, 2)) # 99518 print(round(i, -1)) # 99520 print(round(i, -2)) # 99500 print(round(i, -3)) # 100000
round() arrotonda a un numero pari, non a un arrotondamento comune
Notate che l'arrotondamento con la funzione built-in round() in Python 3 arrotonda ad un numero pari, non ad un arrotondamento generale.
Come scritto nella documentazione ufficiale, 0,5 è arrotondato a 0, 5 è arrotondato a 0, e così via.
print('0.4 =>', round(0.4)) print('0.5 =>', round(0.5)) print('0.6 =>', round(0.6)) # 0.4 => 0 # 0.5 => 0 # 0.6 => 1 print('4 =>', round(4, -1)) print('5 =>', round(5, -1)) print('6 =>', round(6, -1)) # 4 => 0 # 5 => 0 # 6 => 10
La definizione di arrotondamento a un numero pari è la seguente.
Se la frazione è minore di 0,5, arrotondala per difetto; se la frazione è maggiore di 0,5, arrotondala per eccesso; se la frazione è esattamente 0,5, arrotondala al numero pari tra l'arrotondamento per difetto e quello per eccesso.
Rounding – Wikipedia
0,5 non è sempre troncato.
print('0.5 =>', round(0.5)) print('1.5 =>', round(1.5)) print('2.5 =>', round(2.5)) print('3.5 =>', round(3.5)) print('4.5 =>', round(4.5)) # 0.5 => 0 # 1.5 => 2 # 2.5 => 2 # 3.5 => 4 # 4.5 => 4
In alcuni casi, la definizione di arrotondamento a un numero pari non si applica nemmeno all'elaborazione dopo due cifre decimali.
print('0.05 =>', round(0.05, 1)) print('0.15 =>', round(0.15, 1)) print('0.25 =>', round(0.25, 1)) print('0.35 =>', round(0.35, 1)) print('0.45 =>', round(0.45, 1)) # 0.05 => 0.1 # 0.15 => 0.1 # 0.25 => 0.2 # 0.35 => 0.3 # 0.45 => 0.5
Questo è dovuto al fatto che i decimali non possono essere rappresentati esattamente come numeri in virgola mobile, come dichiarato nella documentazione ufficiale.
Il comportamento di round() per i numeri in virgola mobile potrebbe sorprendervi:Per esempio, round(2.675, 2) vi darà 2.67 invece di 2.68 come previsto. Questo non è un bug.:Questo è il risultato del fatto che la maggior parte dei decimali non può essere rappresentata esattamente da numeri in virgola mobile.
round() — Built-in Functions — Python 3.10.2 Documentation
Se volete ottenere un arrotondamento generale o un arrotondamento accurato dei decimali ai numeri pari, potete usare la libreria standard decimal quantize (descritta sotto) o definire una nuova funzione.
Notate anche che round() in Python 2 non è l'arrotondamento ad un numero pari, ma l'arrotondamento.
quantize() della libreria standard decimale
Il modulo decimale della libreria standard può essere usato per gestire numeri decimali esatti in virgola mobile.
Usando il metodo quantize() del modulo decimale, è possibile arrotondare i numeri specificando la modalità di arrotondamento.
- decimal quantize() — Decimal fixed point and floating point arithmetic — Python 3.10.2 Documentation
- Rounding modes — Decimal fixed point and floating point arithmetic — Python 3.10.2 Documentation
I valori impostati per l'argomento di arrotondamento del metodo quantize() hanno i seguenti significati, rispettivamente.
ROUND_HALF_UP
:Arrotondamento generaleROUND_HALF_EVEN
:Arrotondamento ai numeri pari
Il modulo decimale è una libreria standard, quindi non è richiesta alcuna installazione aggiuntiva, ma è necessaria l'importazione.
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN
Creare un oggetto Decimal
Decimal() può essere usato per creare oggetti di tipo Decimal.
Se si specifica un tipo float come argomento, si può vedere come viene effettivamente trattato il valore.
print(Decimal(0.05)) # 0.05000000000000000277555756156289135105907917022705078125 print(type(Decimal(0.05))) # <class 'decimal.Decimal'>
Come mostrato nell'esempio, 0,05 non è trattato esattamente come 0,05. Questo è il motivo per cui la funzione built-in round() descritta sopra arrotonda ad un valore diverso da quello previsto per i valori decimali tra cui 0,05 nell'esempio.
Poiché 0,5 è la metà (-1 potenza di 2), può essere espresso esattamente in notazione binaria.
print(Decimal(0.5)) # 0.5
Se specificate il tipo stringa str invece del tipo float, sarà trattato come il tipo Decimal del valore esatto.
print(Decimal('0.05')) # 0.05
Arrotondamento dei decimali a qualsiasi numero di cifre e arrotondamento ai numeri pari
Chiama quantize() da un oggetto di tipo Decimal per arrotondare il valore.
Il primo argomento di quantize() è una stringa con lo stesso numero di cifre del numero di cifre che volete trovare, come '0.1' o '0.01'.
Inoltre, l'argomento ROUNDING specifica il modo di arrotondamento; se viene specificato ROUND_HALF_UP, viene usato l'arrotondamento generale.
f = 123.456 print(Decimal(str(f)).quantize(Decimal('0'), rounding=ROUND_HALF_UP)) # 123 print(Decimal(str(f)).quantize(Decimal('0.1'), rounding=ROUND_HALF_UP)) # 123.5 print(Decimal(str(f)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)) # 123.46
A differenza della funzione built-in round(), 0,5 è arrotondato a 1.
print('0.4 =>', Decimal(str(0.4)).quantize(Decimal('0'), rounding=ROUND_HALF_UP)) print('0.5 =>', Decimal(str(0.5)).quantize(Decimal('0'), rounding=ROUND_HALF_UP)) print('0.6 =>', Decimal(str(0.6)).quantize(Decimal('0'), rounding=ROUND_HALF_UP)) # 0.4 => 0 # 0.5 => 1 # 0.6 => 1
Se l'argomento di arrotondamento è impostato a ROUND_HALF_EVEN, l'arrotondamento viene eseguito ai numeri pari come nella funzione built-in round().
Come menzionato sopra, se un tipo floating-point float è specificato come argomento di Decimal(), è trattato come un oggetto Decimal con un valore uguale al valore effettivo del tipo float, quindi il risultato dell'uso del metodo quantize() sarà diverso da quello atteso, proprio come la funzione built-in round().
print('0.05 =>', round(0.05, 1)) print('0.15 =>', round(0.15, 1)) print('0.25 =>', round(0.25, 1)) print('0.35 =>', round(0.35, 1)) print('0.45 =>', round(0.45, 1)) # 0.05 => 0.1 # 0.15 => 0.1 # 0.25 => 0.2 # 0.35 => 0.3 # 0.45 => 0.5 print('0.05 =>', Decimal(0.05).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.15 =>', Decimal(0.15).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.25 =>', Decimal(0.25).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.35 =>', Decimal(0.35).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.45 =>', Decimal(0.45).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) # 0.05 => 0.1 # 0.15 => 0.1 # 0.25 => 0.2 # 0.35 => 0.3 # 0.45 => 0.5
Se l'argomento di Decimal() è specificato come una stringa di tipo str, viene trattato come un oggetto Decimal esattamente di quel valore, quindi il risultato è quello atteso.
print('0.05 =>', Decimal(str(0.05)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.15 =>', Decimal(str(0.15)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.25 =>', Decimal(str(0.25)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.35 =>', Decimal(str(0.35)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) print('0.45 =>', Decimal(str(0.45)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)) # 0.05 => 0.0 # 0.15 => 0.2 # 0.25 => 0.2 # 0.35 => 0.4 # 0.45 => 0.4
Poiché 0,5 può essere gestito correttamente dal tipo float, non c'è nessun problema nello specificare il tipo float come argomento di Decimal() quando si arrotonda a un intero, ma è più sicuro specificare il tipo string str quando si arrotonda a un decimale.
Per esempio, 2.675 è in realtà 2.67499…. nel tipo float. Pertanto, se volete arrotondare a due cifre decimali, dovete specificare una stringa a Decimal(), altrimenti il risultato sarà diverso da quello atteso sia che arrotondiate al numero intero più vicino (ROUND_HALF_UP) o ad un numero pari (ROUND_HALF_EVEN).
print(Decimal(2.675)) # 2.67499999999999982236431605997495353221893310546875 print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)) # 2.67 print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)) # 2.68 print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN)) # 2.67 print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN)) # 2.68
Notate che il metodo quantize() restituisce un numero di tipo Decimal, quindi se volete operare su un numero di tipo float, dovete convertirlo in un tipo float usando float(), altrimenti si verificherà un errore.
d = Decimal('123.456').quantize(Decimal('0.01'), rounding=ROUND_HALF_UP) print(d) # 123.46 print(type(d)) # <class 'decimal.Decimal'> # print(1.2 + d) # TypeError: unsupported operand type(s) for +: 'float' and 'decimal.Decimal' print(1.2 + float(d)) # 124.66
Arrotondamento dei numeri interi a qualsiasi numero di cifre e arrotondamento ai numeri pari
Se volete arrotondare a una cifra intera, specificare qualcosa come '10' come primo argomento non vi darà il risultato desiderato.
i = 99518 print(Decimal(i).quantize(Decimal('10'), rounding=ROUND_HALF_UP)) # 99518
Questo perché quantize() esegue l'arrotondamento secondo l'esponente dell'oggetto Decimal, ma l'esponente di Decimal('10') è 0, non 1.
Potete specificare un esponente arbitrario usando E come stringa di esponente (ad esempio, '1E1'). L'esponente esponente può essere controllato nel metodo as_tuple.
print(Decimal('10').as_tuple()) # DecimalTuple(sign=0, digits=(1, 0), exponent=0) print(Decimal('1E1').as_tuple()) # DecimalTuple(sign=0, digits=(1,), exponent=1)
Così com'è, il risultato sarà in notazione esponenziale usando E. Se volete usare la notazione normale, o se volete operare con il tipo int intero dopo l'arrotondamento, usate int() per convertire il risultato.
print(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)) # 9.952E+4 print(int(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))) # 99520 print(int(Decimal(i).quantize(Decimal('1E2'), rounding=ROUND_HALF_UP))) # 99500 print(int(Decimal(i).quantize(Decimal('1E3'), rounding=ROUND_HALF_UP))) # 100000
Se l'argomento di arrotondamento è impostato a ROUND_HALF_UP, si verificherà un arrotondamento generale, ad esempio, 5 sarà arrotondato a 10.
print('4 =>', int(Decimal(4).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))) print('5 =>', int(Decimal(5).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))) print('6 =>', int(Decimal(6).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))) # 4 => 0 # 5 => 10 # 6 => 10
Naturalmente, non c'è nessun problema se lo si specifica come una stringa.
Definire una nuova funzione
Il metodo di usare il modulo decimale è accurato e sicuro, ma se non siete a vostro agio con la conversione dei tipi, potete definire una nuova funzione per ottenere un arrotondamento generale.
Ci sono molti modi possibili per farlo, per esempio la seguente funzione.
def my_round(val, digit=0): p = 10 ** digit return (val * p * 2 + 1) // 2 / p
Se non avete bisogno di specificare il numero di cifre e arrotondate sempre alla prima cifra decimale, potete usare una forma più semplice.
my_round_int = lambda x: int((x * 2 + 1) // 2)
Se hai bisogno di essere preciso, è più sicuro usare il decimale.
Quanto segue è solo per riferimento.
Arrotonda i decimali a qualsiasi numero di cifre.
print(int(my_round(f))) # 123 print(my_round_int(f)) # 123 print(my_round(f, 1)) # 123.5 print(my_round(f, 2)) # 123.46
A differenza del giro, 0,5 diventa 1 come da arrotondamento generale.
print(int(my_round(0.4))) print(int(my_round(0.5))) print(int(my_round(0.6))) # 0 # 1 # 1
Arrotonda i numeri interi a qualsiasi numero di cifre
i = 99518 print(int(my_round(i, -1))) # 99520 print(int(my_round(i, -2))) # 99500 print(int(my_round(i, -3))) # 100000
A differenza del giro, 5 diventa 10 come da arrotondamento comune.
print(int(my_round(4, -1))) print(int(my_round(5, -1))) print(int(my_round(6, -1))) # 0 # 10 # 10
Nota: per valori negativi
Nella funzione di esempio qui sopra, -0,5 è arrotondato a 0.
print(int(my_round(-0.4))) print(int(my_round(-0.5))) print(int(my_round(-0.6))) # 0 # 0 # -1
Ci sono vari modi di pensare all'arrotondamento per i valori negativi, ma se volete rendere -0,5 in -1, potete modificarlo come segue, per esempio
import math def my_round2(val, digit=0): p = 10 ** digit s = math.copysign(1, val) return (s * val * p * 2 + 1) // 2 / p * s print(int(my_round2(-0.4))) print(int(my_round2(-0.5))) print(int(my_round2(-0.6))) # 0 # -1 # -1