Header Ads Widget

Ticker

6/recent/ticker-posts

Cómo redondear un número en Python: truncamiento, aritmética y más

 ¿Recuerda haber aprendido a redondear en la escuela primaria? ¡Yo también! El único problema es que no uso la idea muy a menudo. Como resultado, no siempre recuerdo cómo redondear un número en contextos de programación como Python. Afortunadamente, he armado un pequeño artículo para mí. Con suerte, también obtendrá algo de valor.

Resulta que hay un montón de formas de redondear un número en Python. Por ejemplo, se podría truncar la fracción por completo el uso de un yeso para int: int()Por supuesto, hay opciones más sofisticadas como la round()función que redondea al número par más cercano para valores intermedios como 7.5. Dicho esto, siéntase libre de lanzar su propia solución. He construido mi propia solución “de ida y media-up” con el operador ternario: int(x + .5) if x >= 0 else int(x - .5)Consulte el resto del artículo para obtener más detalles.

Descripción del problema

El redondeo es una de esas operaciones que damos por sentado en la vida cotidiana. Por ejemplo, utilizo Acorns, que redondea mis compras al dólar entero más cercano e invierte el exceso en mi nombre.

Desafortunadamente, redondear a números enteros no es una operación obvia en la programación. No hay un operador para redondeo en la mayoría de los idiomas, y dudo que alguna vez lo haya. En cambio, a menudo tenemos que apoyarnos en una biblioteca o rodar una propia.

Para complicar las cosas, el redondeo no siempre es una operación obvia. Por ejemplo, ¿cómo sabemos cuándo redondear hacia arriba o hacia abajo? La forma en que me enseñaron en la escuela fue redondear números hacia arriba (alejándome de cero) cuando el decimal es .5 o mayor.

Resulta que hay muchas formas diferentes de redondear un número entero. De hecho, encontré este interesante artículo en Electronic Engineering Times que describe varios métodos de redondeo diferentes . Para resumir, aquí hay algunas opciones:

  • Redondear hacia el más cercano : redondear al número más cercano (pero, ¿qué pasa con .5?)
  • Redondeo a la mitad : redondeo hacia el más cercano donde 0,5 se redondea desde cero (por ejemplo, 4,5 se redondea a 5)
  • Redondeo-mitad-abajo : redondeo hacia el más cercano donde 0,5 se redondea hacia cero (por ejemplo, 4,5 se redondea a 4)
  • Redondeo medio par : redondo hacia el más cercano donde 0,5 se redondea hacia el número par más cercano (por ejemplo, 4,5 se redondea a 4 mientras que 5,5 se redondea a 6)
  • Redondo medio impar : redondo hacia el más cercano, donde .5 se redondea hacia el número impar más cercano (por ejemplo, 4.5 se redondea a 5 mientras que 5.5.
  • Ronda-alternativa : ronda hacia el más cercano donde .5 alterna entre redondeo hacia arriba y hacia abajo con el tiempo (por ejemplo, 4.5 rondas a 5 y luego 5.5 rondas a 5)
  • Ronda aleatoria : ronda hacia el más cercano donde .5 se redondea hacia arriba o hacia abajo al azar (por ejemplo, 4.5 podría redondearse a 4 o 5)
  • Round-cieling : redondea todos los decimales hacia el infinito positivo (por ejemplo, 4,3 se redondea a 5 mientras que -4,7 se redondea a -4)
  • Piso redondo: redondee todos los decimales hacia el infinito negativo (p. Ej., 4,7 se redondea a 4 mientras que -4,7 se redondea a -5)
  • Redondear hacia cero : redondea todos los decimales hacia cero (por ejemplo, 4,7 se redondea a 4 mientras que -4,7 se redondea a -4)
  • Redondeo desde cero : redondee todos los decimales desde cero (por ejemplo, 4,3 se redondea a 5 mientras que -4,3 se redondea a -5)

Claramente, hay muchas formas de redondear números. Para los propósitos de este artículo, usaremos el método de "redondeo a la mitad". En otras palabras, números como 3,5, 5,5 y -2,5 se redondearán a 4, 6 y -3, respectivamente.

Soluciones

En este artículo, veremos algunas formas diferentes de redondear números en Python. Como siempre, comenzaremos con las soluciones sencillas o de fuerza bruta. Luego, avanzaremos hacia soluciones más comunes.

Redondeo por truncamiento

Una forma de redondear un número es recortar el lugar decimal mediante el truncamiento:

1
x = int(5.3)  # stores 5

En este ejemplo, xalmacenaremos 5 a medida que recortamos el .3. Si tuviéramos que cambiar el valor de ejemplo a algo que debería redondearse, estaremos decepcionados:

1
x = int(5.7)  # stores 5

Claramente, esta no es la solución de "redondeo a la mitad" que discutimos anteriormente, pero es un buen atajo si solo necesitamos eliminar el lugar decimal (es decir, "redondear hacia cero").

Dicho esto, la simplicidad de esta solución nos brinda un buen beneficio: el truncamiento también funciona para números negativos:

1
x = int(-5.7)  # stores -5

Por supuesto, si queremos una verdadera solución de "redondeo a la mitad", tendremos que probar otra cosa.

Redondeo por flujo de control

Si pensamos en cómo funciona el "redondeo a la mitad", entonces probablemente podamos juntar algunas declaraciones if para hacerlo:

1
2
3
4
5
6
7
x = 5.3
fraction = x - int(x)
if abs(fraction) >= .5:
  offset = 1 - fraction
  x = x + offset
else:
  x = x - fraction

Aquí, podemos calcular la porción fraccionaria de un número usando nuestra solución de truncamiento anterior. En otras palabras, podemos restar el valor truncado del valor real para obtener la fracción. En este caso, int(x)devolverá 5 que restaremos de 5.3. Como resultado, fractionalmacena .3 ( ish ).

Luego, podemos usar esa fracción para realizar algún flujo de control. Por ejemplo, si el valor absoluto de fractiones mayor o igual a .5, sabemos que debemos redondear. Aquí, el valor absoluto explica el hecho de que fractionpodría ser positivo o negativo. De lo contrario, es posible que tengamos que escribir una declaración if un poco más molesta. Si desea obtener más información sobre cómo calcular el valor absoluto en Python, tengo un artículo completo sobre eso .

En cualquier caso, para redondear un número, necesitamos calcular la distancia hasta el siguiente número al que llamamos offsetPodemos calcular eso restando fractionde 1. Ahora, es solo una cuestión de sumar el desplazamiento xy listo.

Por otro lado, si encontramos que el valor absoluto de en fractionrealidad es menor que .5, podemos restar esa fracción directamente de xEsto funcionará independientemente de si xes positivo o negativo.

Si queremos hacer un esfuerzo adicional, podemos convertir xa un número entero. Dicho esto, esto debería hacer el trabajo, salvo los molestos errores de redondeo.

Redondeo por aritmética

Otra forma realmente inteligente de "redondear a la mitad" es aprovechar la solución de truncamiento desde arriba con una ligera modificación:

1
x = int(5.3 + .5)

Aquí, hemos agregado .5 directamente a xSi la porción fraccionaria de xresulta ser .5 o mayor, se xpasará al siguiente número. Luego, cuando truncamos x, lo habremos redondeado con éxito.

Por otro lado, si la porción fraccionaria de xestá por debajo de .5, la porción del número entero de xpermanecerá igual. Como resultado, truncar xtendrá el efecto de redondear el número.

Desafortunadamente, esta solución no funcionará cuando xsea ​​negativa. Para manejar ese caso, necesitaremos algún tipo de rama. Como soy vago y me gustan las frases ingeniosas, voy a optar por el ternario:

1
2
x = 5.3
int(x + .5) if x >= 0 else int(x - .5)

Ahora, si xes negativo, restaremos .5 en lugar de sumarlo. Si hay una solución más inteligente, hágamelo saber en los comentarios.

Redondeo con la round()función

Si escribir un algoritmo de redondeo a mano está fuera de discusión, Python en realidad proporciona una función de redondeo incorporada:

1
round(5.3)

Desafortunadamente, su comportamiento no se corresponde con nuestro algoritmo de "redondeo a la mitad". En cambio, es un poco más complicado. Echemos un vistazo a algunos ejemplos:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
dieciséis
>>> round(.5)
0
>>> round(-.5)
0
>>> round(1.5)
2
>>> round(2.5)
2
>>> round(3.5)
4
>>> round(-1.5)
-2
>>> round(-2.5)
-2
>>> round(-3.5)
-4

Si miramos hacia atrás en nuestra lista de algoritmos de redondeo, encontraremos que los desarrolladores de Python realmente han implementado el algoritmo de “ronda-mitad-par”. Cuando investigué un poco sobre este algoritmo, descubrí que a veces se le llama redondeo bancario , ¡cuanto más sepa!

Honestamente, no hay mucho más que decir sobre esta solución. Sin embargo, es importante tener en cuenta que la función round en Python también puede funcionar para valores de punto flotante. Por ejemplo, podemos redondear al lugar de las décimas de la siguiente manera:

1
2
>>> round(3.52, 1)
3.5

¿Cuan genial es eso?

Actuación

Con las soluciones fuera del camino, echemos un vistazo a cómo funcionan. Para hacer eso, necesitaremos capturar cada solución en una cadena:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
dieciséis
17
18
19
20
21
22
23
24
setup = """
x = 2.5
"""
 
truncation = """
int(x)
"""
 
control_flow = """
fraction = x - int(x)
if abs(fraction) >= .5:
  offset = 1 - fraction
  x + offset
else:
  x - fraction
"""
 
arithmetic = """
int(x + .5) if x >= 0 else int(x - .5)
"""
 
banker = """
round(x)
"""

Con nuestras cadenas listas para funcionar, todo lo que tenemos que hacer es cargar en la timeitbiblioteca y lanzar nuestras pruebas:

1
2
3
4
5
6
7
8
9
>>> import timeit
>>> min(timeit.repeat(setup=setup, stmt=truncation))
0.1537370000005467
>>> min(timeit.repeat(setup=setup, stmt=control_flow))
0.43060659999900963
>>> min(timeit.repeat(setup=setup, stmt=arithmetic))
0.2925704000008409
>>> min(timeit.repeat(setup=setup, stmt=banker))
0.25559939999948256

Quizás como era de esperar, el truncamiento gana en la competencia de velocidad. Sin embargo, la round()función incorporada es bastante rápida. Imagino que es porque la función se implementa en un lenguaje de nivel inferior.

Como siempre, tome estas medidas con cautela. Ejecuté cada uno de ellos en una máquina con Windows 10 con Python 3.7.3. Además, si está interesado en este proceso de prueba de rendimiento, tengo un artículo completo al respecto .

Desafío

Cuando se trata de redondeo, hay un montón de algoritmos diferentes. Y para cada algoritmo, probablemente hay miles de contextos en los que se utilizan. Naturalmente, pensé que sería divertido hacerte aplicar el algoritmo de redondeo en uno de esos contextos, pero pensé que sería más divertido profundizar en otros algoritmos de redondeo.

Para este desafío, le pido que implemente su propio algoritmo de redondeo bancario. En otras palabras, escriba algún código de Python que implemente el mismo estilo de redondeo que proporciona la round()función de Python .

Cuando crea que tiene algo, envuélvalo en una función y pruébelo en las siguientes entradas:

DescripciónEntradaSalida
Cerca de cero0,50
Estuche estándar0,71
Estuche estándar1.21
Incluso redondear1,52
Incluso redondear hacia abajo2.52
Incluso redondear negativo-1,5-2
Incluso redondeo hacia abajo negativo-2,5-2

Luego, cuando esté listo, ¡su solución en los comentarios!

Un pequeño resumen

Por fin, hemos llegado al final de esta publicación. Como siempre, aquí hay una lista de cada solución utilizada en este artículo:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
dieciséis
17
18
x = 17.1
 
# Truncation
int(x)
 
# Control flow rounding
fraction = x - int(x)
if abs(fraction) >= .5:
  offset = 1 - fraction
  x + offset
else:
  x - fraction
 
# Arithmetic rounding
int(x + .5) if x >= 0 else int(x - .5)
 
# Functional rounding
round(x)

Publicar un comentario

0 Comentarios