Avrundning av decimaler och heltal i Python med ”round” och ”Decimal.quantize”.

Företag

Följande förklarar hur man avrundar tal i Python genom att avrunda eller avrunda till ett jämnt tal. Talen antas vara av typen floating point float eller heltal int.

  • inbyggd funktion (t.ex. i ett programmeringsspråk): round()
    • Avrunda decimaler till valfritt antal siffror.
    • Avrunda heltal till valfritt antal siffror.
    • round() avrundar till ett jämnt tal, inte till en vanlig avrundning.
  • standardbibliotekdecimal quantize()
    • DecimalSkapa ett objekt
    • Avrundning av decimaltal till valfritt antal siffror och avrundning till jämna tal.
    • Avrundning av heltal till valfritt antal siffror och avrundning till jämna tal.
  • Definiera en ny funktion
    • Avrunda decimaler till valfritt antal siffror.
    • Avrunda heltal till valfritt antal siffror
    • Anmärkning: För negativa värden

Observera att den inbyggda funktionen round, som nämnts ovan, inte är en allmän avrundning utan en avrundning till ett jämnt tal. Se nedan för mer information.

inbyggd funktion (t.ex. i ett programmeringsspråk): round()

Round() finns som en inbyggd funktion. Den kan användas utan att importera några moduler.

Det första argumentet är det ursprungliga talet och det andra argumentet är antalet siffror (hur många siffror som ska avrundas).

Avrunda decimaler till valfritt antal siffror.

Följande är ett exempel på bearbetning för floating point float-typen.

Om det andra argumentet utelämnas avrundas det till ett heltal. Typen blir också en heltalsint-typ.

f = 123.456

print(round(f))
# 123

print(type(round(f)))
# <class 'int'>

Om det andra argumentet anges returnerar den en floating point float-typ.

Om ett positivt heltal anges, anges decimalplatsen; om ett negativt heltal anges, anges heltalsplatsen. -1 avrundas till närmaste tiondel, -2 avrundas till närmaste hundradel och 0 avrundas till ett heltal (den första platsen), men returnerar en floattyp, till skillnad från när den utelämnas.

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'>

Avrunda heltal till valfritt antal siffror.

Följande är ett exempel på behandling av typen heltal int.

Om det andra argumentet utelämnas, eller om 0 eller ett positivt heltal anges, returneras det ursprungliga värdet som det är. Om ett negativt heltal anges avrundas värdet till motsvarande heltalssiffra. I båda fallen returneras ett heltal av typen 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() avrundar till ett jämnt tal, inte till en vanlig avrundning.

Observera att avrundning med den inbyggda funktionen round() i Python 3 avrundar till ett jämnt tal, inte till en allmän avrundning.

Enligt den officiella dokumentationen avrundas 0,5 till 0, 5 avrundas till 0 och så vidare.

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

Definitionen av avrundning till ett jämnt tal är följande.

Om bråket är mindre än 0,5 avrundas det nedåt; om bråket är större än 0,5 avrundas det uppåt; om bråket är exakt 0,5 avrundas det uppåt till det jämna talet mellan avrundning nedåt och avrundning uppåt.
Rounding – Wikipedia

0,5 är inte alltid trunkerad.

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

I vissa fall gäller definitionen av avrundning till ett jämnt tal inte ens för bearbetning efter två decimaler.

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

Detta beror på att decimaltal inte kan representeras exakt som flyttal, vilket anges i den officiella dokumentationen.

Beteendet hos round() för flyttal kan förvåna dig.:Till exempel ger runda(2,675, 2) 2,67 i stället för 2,68 som förväntat. Detta är inget fel.:Detta beror på att de flesta decimaler inte kan representeras exakt av flyttalstal.
round() — Built-in Functions — Python 3.10.2 Documentation

Om du vill uppnå allmän avrundning eller exakt avrundning av decimaler till jämna tal kan du använda standardbibliotekets decimal quantize (beskrivs nedan) eller definiera en ny funktion.

Observera också att round() i Python 2 inte avrundar till ett jämnt tal, utan avrundar.

quantize() i standardbibliotekets decimaltal

Decimalmodulen i standardbiblioteket kan användas för att hantera exakta decimala flyttalstal.

Med hjälp av metoden quantize() i decimalmodulen är det möjligt att avrunda tal genom att ange avrundningsläget.

De inställda värdena för argumentet avrundning i metoden quantize() har följande betydelser.

  • ROUND_HALF_UP:Allmän avrundning
  • ROUND_HALF_EVEN:Avrundning till jämna tal

Decimalmodulen är ett standardbibliotek, så ingen ytterligare installation krävs, men import är nödvändig.

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

Skapa ett decimalobjekt

Decimal() kan användas för att skapa objekt av typen Decimal.

Om du anger en float-typ som argument kan du se vad värdet faktiskt behandlas som.

print(Decimal(0.05))
# 0.05000000000000000277555756156289135105907917022705078125

print(type(Decimal(0.05)))
# <class 'decimal.Decimal'>

Som framgår av exemplet behandlas 0,05 inte som exakt 0,05. Detta är anledningen till att den inbyggda funktionen round() som beskrivs ovan avrundas till ett annat värde än förväntat för decimalvärden som 0,05 i exemplet.

Eftersom 0,5 är hälften (-1 potens av 2) kan det uttryckas exakt i binär notation.

print(Decimal(0.5))
# 0.5

Om du anger strängtypen str i stället för float-typen kommer den att behandlas som decimaltypen för det exakta värdet.

print(Decimal('0.05'))
# 0.05

Avrundning av decimaltal till valfritt antal siffror och avrundning till jämna tal.

Kalla quantize() från ett objekt av typen Decimal för att avrunda värdet.

Det första argumentet för quantize() är en sträng med samma antal siffror som det antal siffror du vill hitta, till exempel ”0,1” eller ”0,01”.

Dessutom anger argumentet ROUNDING avrundningsmetoden; om ROUND_HALF_UP anges används allmän avrundning.

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

Till skillnad från den inbyggda funktionen round() avrundas 0,5 till 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

Om argumentet avrundning är inställt på ROUND_HALF_EVEN avrundas till jämna tal som i den inbyggda funktionen round().

Som nämnts ovan, om en floating point-float-typ anges som argument för Decimal() behandlas den som ett Decimal-objekt med ett värde som är lika med det faktiska värdet för float-typen, så resultatet av att använda Quantisera() kommer att bli annorlunda än förväntat, precis som den inbyggda funktionen 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

Om argumentet för Decimal() anges som en sträng av typen str behandlas det som ett Decimal-objekt med exakt det värdet, så resultatet blir som förväntat.

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

Eftersom 0,5 kan hanteras korrekt av typen float är det inget problem att ange float-typen som argument för Decimal() vid avrundning till ett heltal, men det är säkrare att ange typen sträng str vid avrundning till en decimal.

Exempelvis är 2,675 faktiskt 2,67499…. i floatform. Om du vill avrunda till två decimaler måste du därför ange en sträng till Decimal(), annars kommer resultatet att skilja sig från det förväntade resultatet oavsett om du avrundar till närmaste heltal (ROUND_HALF_UP) eller till ett jämnt tal (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

Observera att metoden quantize() returnerar ett tal av decimaltyp, så om du vill använda ett tal av floattyp måste du konvertera det till en floattyp med hjälp av float(), annars uppstår ett fel.

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

Avrundning av heltal till valfritt antal siffror och avrundning till jämna tal.

Om du vill avrunda till en heltalssiffra får du inte det önskade resultatet om du anger något som ”10” som första argument.

i = 99518

print(Decimal(i).quantize(Decimal('10'), rounding=ROUND_HALF_UP))
# 99518

Detta beror på att quantize() avrundar enligt exponenten för Decimal-objektet, men exponenten för Decimal('10') är 0, inte 1.

Du kan ange en godtycklig exponent genom att använda E som exponentsträng (t.ex. ”1E1”). Exponenten exponent kan kontrolleras i as_tuple-metoden.

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)

Som det är nu kommer resultatet att vara i exponentiell notation med E. Om du vill använda normal notation, eller om du vill arbeta med heltal av typen int efter avrundning, använd int() för att konvertera resultatet.

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

Om argumentet avrundning är inställt på ROUND_HALF_UP kommer allmän avrundning att ske, t.ex. kommer 5 att avrundas till 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

Det är naturligtvis inget problem om du anger det som en sträng.

Definiera en ny funktion

Metoden med decimalmodulen är exakt och säker, men om du inte är bekväm med typkonvertering kan du definiera en ny funktion för att uppnå allmän avrundning.

Det finns många olika sätt att göra detta, till exempel följande funktion.

def my_round(val, digit=0):
    p = 10 ** digit
    return (val * p * 2 + 1) // 2 / p

Om du inte behöver ange antalet siffror och alltid avrundar till första decimalen kan du använda en enklare form.

my_round_int = lambda x: int((x * 2 + 1) // 2)

Om du behöver vara exakt är det säkrare att använda decimaltal.

Nedanstående är endast avsett som referens.

Avrunda decimaler till valfritt antal siffror.

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

Till skillnad från avrundning blir 0,5 till 1 enligt allmän avrundning.

print(int(my_round(0.4)))
print(int(my_round(0.5)))
print(int(my_round(0.6)))
# 0
# 1
# 1

Avrunda heltal till valfritt antal siffror

i = 99518

print(int(my_round(i, -1)))
# 99520

print(int(my_round(i, -2)))
# 99500

print(int(my_round(i, -3)))
# 100000

Till skillnad från avrundning blir 5 10 enligt vanlig avrundning.

print(int(my_round(4, -1)))
print(int(my_round(5, -1)))
print(int(my_round(6, -1)))
# 0
# 10
# 10

Anmärkning: För negativa värden

I exempelfunktionen ovan avrundas -0,5 till 0.

print(int(my_round(-0.4)))
print(int(my_round(-0.5)))
print(int(my_round(-0.6)))
# 0
# 0
# -1

Det finns olika sätt att tänka på avrundning för negativa värden, men om du vill göra -0,5 till -1 kan du ändra det på följande sätt, t.ex.

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
Copied title and URL