Afronden van decimalen en gehele getallen in Python met “round” en “Decimal.quantize

Bedrijf

Hieronder wordt uitgelegd hoe je getallen in Python kunt afronden door afronding of afronding op een even getal. De getallen worden verondersteld van het type floating point float of integer int te zijn.

  • ingebouwde functie (bv. in een programmeertaal): round()
    • Rond decimalen af op een willekeurig aantal cijfers.
    • Rond gehele getallen af op een willekeurig aantal cijfers.
    • rond() af op een even getal, niet op een gewone afronding
  • standaardbibliotheekdecimal quantize()
    • DecimalEen object creëren
    • Afronden van decimalen op een willekeurig aantal cijfers en afronden op even getallen
    • Afronden van gehele getallen op een willekeurig aantal cijfers en afronden op even getallen
  • Definieer een nieuwe functie
    • Rond decimalen af op een willekeurig aantal cijfers.
    • Rond gehele getallen af op een willekeurig aantal cijfers
    • Opmerking: Voor negatieve waarden

Merk op dat, zoals hierboven vermeld, de ingebouwde functie afronden geen algemene afronding is, maar een afronding op een even getal. Zie hieronder voor details.

ingebouwde functie (bv. in een programmeertaal): round()

Round() wordt aangeboden als een ingebouwde functie. Ze kan worden gebruikt zonder modules te importeren.

Het eerste argument is het oorspronkelijke getal, en het tweede argument is het aantal cijfers (op hoeveel cijfers moet worden afgerond).

Rond decimalen af op een willekeurig aantal cijfers.

Het volgende is een voorbeeld van verwerking voor het vlottertype met drijvende komma.

Indien het tweede argument wordt weggelaten, wordt het afgerond op een geheel getal. Het type wordt ook een geheel getal van het type int.

f = 123.456

print(round(f))
# 123

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

Indien het tweede argument is gespecificeerd, wordt een vlottertype met drijvende komma teruggegeven.

Als een positief geheel getal wordt opgegeven, wordt de decimale plaats opgegeven; als een negatief geheel getal wordt opgegeven, wordt de gehele plaats opgegeven. -1 rondt af naar het dichtstbijzijnde tiende, -2 rondt af naar het dichtstbijzijnde honderdste, en 0 rondt af naar een geheel getal (de eerste plaats), maar geeft een float-type terug, in tegenstelling tot wanneer het wordt weggelaten.

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

Rond gehele getallen af op een willekeurig aantal cijfers.

Het volgende is een voorbeeld van verwerking voor het integer int type.

Indien het tweede argument wordt weggelaten, of indien 0 of een positief geheel getal wordt gespecificeerd, wordt de oorspronkelijke waarde als zodanig teruggegeven. Indien een negatief geheel getal wordt gespecificeerd, wordt afgerond naar het overeenkomstige gehele cijfer. In beide gevallen wordt een geheel getal van het type int teruggegeven.

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

rond() af op een even getal, niet op een gewone afronding

Merk op dat afronden met de ingebouwde round() functie in Python 3 afrondt naar een even getal, niet naar een algemene afronding.

Zoals geschreven in de officiële documentatie, wordt 0.5 afgerond naar 0, 5 wordt afgerond naar 0, enzovoort.

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

De definitie van afronden op een even getal is als volgt.

Als de breuk kleiner is dan 0,5, rondt u naar beneden af; als de breuk groter is dan 0,5, rondt u naar boven af; als de breuk precies 0,5 is, rondt u naar boven af op het even getal tussen afronding naar beneden en afronding naar boven.
Rounding – Wikipedia

0,5 is niet altijd afgekapt.

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 sommige gevallen is de definitie van afronding op een even getal niet eens van toepassing op verwerking na twee decimalen.

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

Dit is te wijten aan het feit dat decimalen niet exact kunnen worden voorgesteld als drijvende komma getallen, zoals vermeld in de officiële documentatie.

Het gedrag van round() voor drijvende komma getallen kan u verrassen:Bijvoorbeeld, afrond(2.675, 2) zal u 2.67 geven in plaats van 2.68 zoals verwacht. Dit is geen fout.:Dit is een gevolg van het feit dat de meeste decimalen niet exact kunnen worden weergegeven door drijvende komma getallen.
round() — Built-in Functions — Python 3.10.2 Documentation

Als u algemene afronding of nauwkeurige afronding van decimalen naar even getallen wilt bereiken, kunt u de standaardbibliotheek decimaal kwantiseren gebruiken (hieronder beschreven) of een nieuwe functie definiëren.

Merk ook op dat round() in Python 2 geen afronding is naar een even getal, maar afronding.

quantize() van de standaard bibliotheek decimaal

De decimale module van de standaard bibliotheek kan worden gebruikt om exacte decimale drijvende komma getallen te verwerken.

Met behulp van de methode quantize() van de decimale module is het mogelijk getallen af te ronden door de afrondingsmodus op te geven.

De ingestelde waarden voor het argument afronding van de methode quantize() hebben respectievelijk de volgende betekenissen.

  • ROUND_HALF_UP:Algemene afronding
  • ROUND_HALF_EVEN:Afronden op even getallen

De decimale module is een standaard bibliotheek, dus geen extra installatie is nodig, maar importeren is wel noodzakelijk.

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

Een Decimaal object maken

Decimal() kan gebruikt worden om objecten van het type Decimal te maken.

Als je een float type als argument specificeert, kan je zien wat de waarde eigenlijk als behandeld wordt.

print(Decimal(0.05))
# 0.05000000000000000277555756156289135105907917022705078125

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

Zoals in het voorbeeld te zien is, wordt 0.05 niet behandeld als precies 0.05. Dit is de reden waarom de hierboven beschreven ingebouwde functie round() afrondt op een andere waarde dan verwacht voor decimale waarden waaronder 0,05 in het voorbeeld.

Aangezien 0,5 de helft is (-1 macht van 2), kan het precies worden uitgedrukt in binaire notatie.

print(Decimal(0.5))
# 0.5

Indien u het string type str specificeert in plaats van het float type, zal het behandeld worden als het Decimal type van de exacte waarde.

print(Decimal('0.05'))
# 0.05

Afronden van decimalen op een willekeurig aantal cijfers en afronden op even getallen

Roep quantize() op vanuit een object van het type Decimal om de waarde af te ronden.

Het eerste argument van quantize() is een tekenreeks met hetzelfde aantal cijfers als het aantal cijfers dat je wilt vinden, zoals '0.1' of '0.01'.

Bovendien specificeert het argument ROUNDING de afrondingsmodus; indien ROUND_HALF_UP is gespecificeerd, wordt algemene afronding gebruikt.

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

In tegenstelling tot de ingebouwde functie round(), wordt 0,5 afgerond op 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

Indien het argument afronden is ingesteld op ROUND_HALF_EVEN, wordt afgerond op even getallen zoals in de ingebouwde functie round().

Zoals hierboven vermeld, indien een floating-point float type gespecificeerd is als argument van Decimal(), wordt het behandeld als een Decimal object met een waarde gelijk aan de werkelijke waarde van het float type, dus het resultaat van het gebruik van de quantize() methode zal anders zijn dan verwacht, net zoals de ingebouwde functie 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

Indien het argument van Decimal() gespecificeerd is als een string van het type str, wordt het behandeld als een Decimal object met exact die waarde, dus het resultaat is zoals verwacht.

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

Aangezien 0.5 correct kan behandeld worden door het type float, is er geen probleem om het type float te specificeren als argument van Decimal() bij afronding naar een geheel getal, maar het is veiliger om het type string str te specificeren bij afronding naar een decimaal getal.

Bijvoorbeeld, 2.675 is eigenlijk 2.67499…. in float type. Daarom, als je wilt afronden naar twee decimalen, moet je een string specificeren aan Decimal(), anders zal het resultaat anders zijn dan het verwachte resultaat of je nu afrondt naar het dichtstbijzijnde hele getal (ROUND_HALF_UP) of naar een even getal (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

Merk op dat de methode quantize() een getal van het type Decimal teruggeeft, dus als je een getal van het type float wilt gebruiken, moet je het omzetten in een getal van het type float met float(), anders zal er een fout optreden.

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

Afronden van gehele getallen op een willekeurig aantal cijfers en afronden op even getallen

Indien u wilt afronden naar een geheel getal, zal het specificeren van iets als '10' als eerste argument niet het gewenste resultaat opleveren.

i = 99518

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

Dat komt omdat quantize() afrondt volgens de exponent van het Decimal object, maar de exponent van Decimal('10') is 0, niet 1.

Je kunt een willekeurige exponent opgeven door E als exponent string te gebruiken (bv. '1E1'). De exponent exponent kan worden gecontroleerd in de as_tuple methode.

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)

Zoals het nu is, zal het resultaat in exponentiële notatie zijn met E. Indien u normale notatie wilt gebruiken, of indien u wilt werken met het gehele intype na afronding, gebruik dan int() om het resultaat om te zetten.

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

Indien het argument afronding ingesteld is op ROUND_HALF_UP, zal een algemene afronding gebeuren, bv. 5 zal afgerond worden tot 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

Natuurlijk is er geen probleem als je het als een string specificeert.

Definieer een nieuwe functie

De methode waarbij de decimale module wordt gebruikt is nauwkeurig en veilig, maar als u niet vertrouwd bent met type-omzetting, kunt u een nieuwe functie definiëren om algemene afronding te bereiken.

Er zijn vele manieren om dit te doen, bijvoorbeeld de volgende functie.

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

Als u het aantal cijfers niet hoeft op te geven en altijd afrondt op de eerste decimaal, kunt u een eenvoudiger vorm gebruiken.

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

Als u precies moet zijn, is het veiliger om decimalen te gebruiken.

Het volgende is alleen ter referentie.

Rond decimalen af op een willekeurig aantal cijfers.

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

In tegenstelling tot afronden, 0.5 wordt 1 volgens algemeen afronden.

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

Rond gehele getallen af op een willekeurig aantal cijfers

i = 99518

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

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

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

In tegenstelling tot afronden, wordt 5 10 volgens de gebruikelijke afronding.

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

Opmerking: Voor negatieve waarden

In de voorbeeldfunctie hierboven wordt -0,5 afgerond op 0.

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

Er zijn verschillende manieren om te denken over afronding voor negatieve waarden, maar als je van -0.5 -1 wilt maken, kun je het als volgt aanpassen, bijvoorbeeld

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