Arrodoniment de nombres decimals i enters en Python amb “round” i “Decimal.quantize

Negocis

A continuació s’explica com arrodonir els nombres a Python arrodonint o arrodonint a un nombre parell. Se suposa que els nombres són de tipus float de coma flotant o enter int.

  • funció integrada (per exemple, en llenguatge de programació):round()
    • Arrodoneix els decimals a qualsevol nombre de dígits.
    • Arrodoneix nombres enters a qualsevol nombre de dígits.
    • round() arrodoneix a un nombre parell, no a un arrodonit comú
  • biblioteca estàndarddecimalquantize()
    • DecimalCreació d’un objecte
    • Arrodoniment de decimals a qualsevol nombre de dígits i arrodonit a nombres parells
    • Arrodoniment de nombres enters a qualsevol nombre de dígits i arrodonit a nombres parells
  • Definiu una nova funció
    • Arrodoneix els decimals a qualsevol nombre de dígits.
    • Arrodoneix nombres enters a qualsevol nombre de dígits
    • Nota: per a valors negatius

Tingueu en compte que, com s’ha esmentat anteriorment, la funció integrada arrodoniment no és un arrodoniment general, sinó un arrodoniment a un nombre parell. Vegeu a continuació per obtenir més informació.

funció integrada (per exemple, en llenguatge de programació):round()

Round() es proporciona com a funció integrada. Es pot utilitzar sense importar cap mòdul.

El primer argument és el nombre original i el segon argument és el nombre de dígits (a quants dígits arrodonir).

Arrodoneix els decimals a qualsevol nombre de dígits.

El següent és un exemple de processament per al tipus flotant de coma flotant.

Si s’omet el segon argument, s’arrodoneix a un nombre enter. El tipus també es converteix en un tipus enter enter.

f = 123.456

print(round(f))
# 123

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

Si s’especifica el segon argument, retorna un tipus flotant de coma flotant.

Si s’especifica un nombre enter positiu, s’especifica la posició decimal; si s’especifica un nombre enter negatiu, s’especifica el lloc de l’enter. -1 arrodonida a la dècima més propera, -2 arrodonides a la centèsima més propera i 0 arrodonides a un nombre enter (el primer lloc), però retorna un tipus flotant, a diferència de quan s’omet.

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

Arrodoneix nombres enters a qualsevol nombre de dígits.

El següent és un exemple de processament per al tipus enter int.

Si s’omet el segon argument, o si s’especifica 0 o un nombre enter positiu, el valor original es retorna tal qual. Si s’especifica un nombre enter negatiu, s’arrodoneix al dígit enter corresponent. En ambdós casos, es retorna un tipus enter enter.

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() arrodoneix a un nombre parell, no a un arrodonit comú

Tingueu en compte que l’arrodoniment amb la funció round() integrada a Python 3 arrodoneix a un nombre parell, no a un arrodoniment general.

Tal com està escrit a la documentació oficial, 0,5 s’arrodoneix a 0, 5 s’arrodoneix a 0 i així successivament.

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 definició d’arrodoniment a un nombre parell és la següent.

Si la fracció és inferior a 0,5, arrodoneix-la per baix; si la fracció és superior a 0,5, arrodoneix-la; si la fracció és exactament 0,5, arrodoniu-la al nombre parell entre l’arrodoniment cap avall i l’arrodoniment cap amunt.
Rounding – Wikipedia

0,5 no sempre està truncat.

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

En alguns casos, la definició d’arrodoniment a un nombre parell ni tan sols s’aplica al processament després de dos decimals.

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

Això es deu al fet que els decimals no es poden representar exactament com a nombres de coma flotant, tal com s’indica a la documentació oficial.

El comportament de round() per als números de coma flotant us pot sorprendre:Per exemple, arrodonir (2,675, 2) us donarà 2,67 en lloc de 2,68 com s’esperava. Això no és un error.:Això és el resultat del fet que la majoria de decimals no es poden representar exactament amb nombres de coma flotant.
round() — Built-in Functions — Python 3.10.2 Documentation

Si voleu aconseguir un arrodoniment general o un arrodoniment precís de decimals a nombres parells, podeu utilitzar la quantificació decimal estàndard de la biblioteca (que es descriu a continuació) o definir una funció nova.

Tingueu en compte també que round() a Python 2 no s’arrodoneix a un nombre parell, sinó que arrodoneix.

quantize() del decimal de la biblioteca estàndard

El mòdul decimal de la biblioteca estàndard es pot utilitzar per gestionar nombres decimals de coma flotant exactes.

Mitjançant el mètode quantize() del mòdul decimal, és possible arrodonir nombres especificant el mode d’arrodoniment.

Els valors establerts per a l’arrodoniment d’arguments del mètode quantize() tenen els significats següents, respectivament.

  • ROUND_HALF_UP:Arrodonit general
  • ROUND_HALF_EVEN:Arrodonint a nombres parells

El mòdul decimal és una biblioteca estàndard, de manera que no cal cap instal·lació addicional, però cal importar-lo.

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

Creació d’un objecte decimal

Decimal() es pot utilitzar per crear objectes de tipus Decimal.

Si especifiqueu un tipus flotant com a argument, podeu veure com es tracta realment el valor.

print(Decimal(0.05))
# 0.05000000000000000277555756156289135105907917022705078125

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

Com es mostra a l’exemple, 0,05 no es tracta exactament com a 0,05. Aquesta és la raó per la qual la funció integrada round() descrita anteriorment s’ha arrodonit a un valor diferent del que s’esperava per als valors decimals, inclòs 0,05 a l’exemple.

Com que 0,5 és la meitat (-1 potència de 2), es pot expressar exactament en notació binària.

print(Decimal(0.5))
# 0.5

Si especifiqueu el tipus de cadena str en lloc del tipus flotant, es tractarà com el tipus decimal del valor exacte.

print(Decimal('0.05'))
# 0.05

Arrodoniment de decimals a qualsevol nombre de dígits i arrodonit a nombres parells

Truqueu quantize() des d’un objecte de tipus Decimal per arrodonir el valor.

El primer argument de quantize() és una cadena amb el mateix nombre de dígits que el nombre de dígits que voleu trobar, com ara ‘0,1’ o ‘0,01’.

A més, l’argument ROUNDING especifica el mode d’arrodoniment; si s’especifica ROUND_HALF_UP, s’utilitza l’arrodoniment general.

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 diferència de la funció integrada round(), 0,5 s’arrodoneix 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

Si l’argument arrodonit s’estableix a ROUND_HALF_EVEN, l’arrodoniment es realitza a nombres parells com a la funció integrada round().

Com s’ha esmentat anteriorment, si s’especifica un tipus flotant de coma flotant com a argument de Decimal(), es tracta com un objecte Decimal amb un valor igual al valor real del tipus flotant, de manera que el resultat d’utilitzar quantize() El mètode serà diferent del que s’espera, igual que la funció integrada 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

Si l’argument de Decimal() s’especifica com una cadena de tipus str, es tracta com un objecte Decimal d’aquest valor exactament, de manera que el resultat és l’esperat.

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

Com que el tipus float pot gestionar correctament 0,5, no hi ha cap problema a especificar el tipus float com a argument de Decimal() quan s’arrodoni a un nombre enter, però és més segur especificar el tipus de cadena en arrodonir a un decimal.

Per exemple, 2.675 és en realitat 2.67499…. en tipus flotant. Per tant, si voleu arrodonir a dos decimals, heu d’especificar una cadena a Decimal(), en cas contrari, el resultat serà diferent del resultat esperat tant si arrodoniu al nombre sencer més proper (ROUND_HALF_UP) com a un nombre parell (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

Tingueu en compte que el mètode quantize() retorna un nombre de tipus Decimal, de manera que si voleu operar amb un nombre de tipus flotant, haureu de convertir-lo en un tipus flotant mitjançant float(), en cas contrari es produirà un error.

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

Arrodoniment de nombres enters a qualsevol nombre de dígits i arrodonit a nombres parells

Si voleu arrodonir a un dígit sencer, especificant alguna cosa com “10” com a primer argument no us donarà el resultat desitjat.

i = 99518

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

Això es deu al fet que quantize() realitza l’arrodoniment segons l’exponent de l’objecte Decimal, però l’exponent de Decimal(’10’) és 0, no 1.

Podeu especificar un exponent arbitrari utilitzant E com a cadena d’exponents (p. ex., ‘1E1’). L’exponent exponent es pot comprovar amb el mètode 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)

Tal com és, el resultat estarà en notació exponencial utilitzant E. Si voleu utilitzar la notació normal, o si voleu operar amb el tipus enter int després d’arrodonir, utilitzeu int() per convertir el resultat.

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

Si l’arrodoniment de l’argument s’estableix a ROUND_HALF_UP, es produirà un arrodoniment general, per exemple, 5 s’arrodonirà 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

Per descomptat, no hi ha cap problema si ho especifiqueu com a cadena.

Definiu una nova funció

El mètode d’ús del mòdul decimal és precís i segur, però si no us sentiu còmode amb la conversió de tipus, podeu definir una funció nova per aconseguir l’arrodoniment general.

Hi ha moltes maneres possibles de fer-ho, per exemple, la funció següent.

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

Si no cal especificar el nombre de dígits i arrodonir sempre al primer decimal, podeu utilitzar una forma més senzilla.

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

Si necessiteu ser precís, és més segur utilitzar decimals.

El següent és només per a referència.

Arrodoneix els decimals a qualsevol nombre de dígits.

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 diferència de la ronda, 0,5 es converteix en 1 segons l’arrodoniment general.

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

Arrodoneix nombres enters a qualsevol nombre de dígits

i = 99518

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

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

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

A diferència de la ronda, 5 es converteix en 10 segons l’arrodoniment habitual.

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

Nota: per a valors negatius

A la funció d’exemple anterior, -0,5 s’arrodoneix a 0.

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

Hi ha diverses maneres de pensar sobre l’arrodoniment de valors negatius, però si voleu convertir -0,5 en -1, podeu modificar-lo de la següent manera, per exemple

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