() translation by (you can also view the original English article)
Incluso las operaciones matemáticas más básicas pueden dar algunas veces un resultado erróneo. Esto sucede debido a limitaciones en almacenar el valor exacto de algunos números. Puedes superar estas limitaciones usando el módulo decimal en Python. De manera similar, ni el módulo math ni el cmath que aprendimos en el último tutorial pueden ayudarnos haciendo aritmética basada en fracciones. Sin embargo, el módulo de fracciones en Python hace exactamente eso.
En este tutorial, aprenderás sobre ambos módulos y las diferentes funciones que hacen disponibles.
¿Por Qué Necesitamos un Módulo Decimal?
Probablemente te estés preguntando por qué necesitamos un módulo para hacer aritmética básica con números decimales cuando ya podemos hacer lo mismo usando flotantes.
Antes de responder esta pregunta, quiero que adivines el valor resultante si tecleas 0.1 + 0.2
en la consola de Python. Si adivinaste que la salida debería ser 0.3, te sorprenderás cuando revises el resultado real, que es 0.30000000000000004. Puedes intentar algunos otros cálculos como 0.05 + 0.1
y obtendrás 0.15000000000000002.
Para entender qué está pasando aquí, intenta representar 1/3
en forma decimal, y notarás que el número es de hecho no determinante en base 10. De manera similar, algunos números como 0.1 o 1/10 son no determinantes en base 2. Ya que estos números aún necesitan ser representados de alguna forma, unas cuantas aproximaciones son realizadas mientras se almacenan, lo que resulta en esos errores.
El número 0.30000000000000004 es de hecho bastante cercano a 0.3. así que podemos salirnos con esta aproximación la mayoría del tiempo. Desafortunadamente, esta aproximación no va a servir cuando estés simulando un lanzamiento de satélite o lidiando con dinero. Otro problema con estas aproximaciones es que los errores se siguen apilando.
Para obtener resultados precisos como a los que estamos acostumbrados cuando se hacen cálculos a mano, necesitamos algo que soporte aritmética decimal de punto flotante rápida y correctamente redondeada, y el módulo decimal hace exactamente eso.
Usando el Módulo Decimal
Antes de usar el módulo, necesitas importarlo primero. Después de eso, puedes crear decimales desde enteros, cadenas, flotantes o tuplas. Cuando el decimal es construido desde un entero o un flotante, hay una conversión exacta del valor de ese número. Echa un vistazo a los ejemplos de abajo para ver a qué me refiero:
1 |
from decimal import Decimal |
2 |
|
3 |
Decimal(121) |
4 |
# returns Decimal('121')
|
5 |
|
6 |
Decimal(0.05) |
7 |
# returns Decimal('0.05000000000000000277555756')
|
8 |
|
9 |
Decimal('0.05') |
10 |
# returns Decimal('0.05')
|
11 |
|
12 |
Decimal((0, (8, 3, 2, 4), -3)) |
13 |
# returns Decimal('8.324')
|
14 |
|
15 |
Decimal((1, (8, 3, 2, 4), -1)) |
16 |
# returns Decimal('-832.4')
|
Como puedes ver, el valor de Decimal (0.05)
es ligeramente diferente de Decimal ('0.05')
. Esto significa que cuando sumas 0.05 y 0.1, deberías usar decimal.Decimal('0.05')
y decimal.Decimal('0.1')
para construir los decimales.
1 |
from decimal import Decimal |
2 |
|
3 |
Decimal('0.05') + Decimal('0.1') |
4 |
# returns Decimal('0.15')
|
5 |
|
6 |
Decimal(0.05) + Decimal(0.1) |
7 |
# returns Decimal('0.1500000000000000083266726847')
|
Ahora que puedes realizar varias operaciones sobre decimales, podrías querer controlar la precisión del redondeo para esas operaciones. Esto puede ser hecho usando la función getcontext()
. Esta función también te permite establecer el valor de las opciones de precisión y redondeo, entre otras cosas.
Por favor ten en mente que tanto como el redondeo y la precisión entran en juego solo durante operaciones aritméticas y no mientras estás creando los decimales mismos.
1 |
import decimal |
2 |
from decimal import Decimal, getcontext |
3 |
|
4 |
Decimal(1) / Decimal(13) |
5 |
# returns Decimal('0.07692307692307692307692307692')
|
6 |
|
7 |
getcontext().prec = 10 |
8 |
|
9 |
Decimal(0.03) |
10 |
# returns Decimal('0.02999999999999999888977697537')
|
11 |
|
12 |
Decimal(1) / Decimal(7) |
13 |
# returns Decimal('0.1428571429')
|
14 |
|
15 |
getcontext().rounding = decimal.ROUND_DOWN |
16 |
|
17 |
Decimal(1) / Decimal(7) |
18 |
# returns Decimal('0.1428571428')
|
19 |
También puedes usar algunas funciones matemáticas como sqrt()
, exp()
, y log()
con decimales. Aquí hay algunos ejemplos.
1 |
import decimal |
2 |
from decimal import Decimal, getcontext |
3 |
|
4 |
Decimal(2).sqrt() |
5 |
# returns Decimal('1.414213562373095048801688724')
|
6 |
|
7 |
getcontext().prec = 4 |
8 |
|
9 |
Decimal('2').sqrt() |
10 |
# returns Decimal('1.414')
|
11 |
|
12 |
Decimal('2000').log10() |
13 |
# returns Decimal('3.301')
|
Usando el Modulo Fracciones
Algunas veces, podrías encarar situaciones en donde necesitas realizar varias operaciones en fracciones o el resultado final necesita ser una fracción. El módulo fracciones puede ser de gran ayuda en estos casos. Este te permite crear una instancia Fraction
desde números, flotantes, decimales e incluso cadenas. Justo como el módulo decimal, hay algunos problemas con este módulo también cuando de trata de crear fracciones desde flotantes. Aquí hay unos cuantos ejemplos:
1 |
from fractions import Fraction |
2 |
from decimal import Decimal |
3 |
|
4 |
Fraction(11, 35) |
5 |
# returns Fraction(11, 35)
|
6 |
|
7 |
Fraction(10, 18) |
8 |
# returns Fraction(5, 9)
|
9 |
|
10 |
Fraction('8/25') |
11 |
# returns Fraction(8, 25)
|
12 |
|
13 |
Fraction(1.13) |
14 |
# returns Fraction(1272266894732165, 1125899906842624)
|
15 |
|
16 |
Fraction('1.13') |
17 |
# returns Fraction(113, 100)
|
18 |
|
19 |
Fraction(Decimal('1.13')) |
20 |
# returns Fraction(113, 100)
|
También puedes realizar operaciones matemáticas simples como suma y resta de fracciones justo como números regulares.
1 |
from fractions import Fraction |
2 |
|
3 |
Fraction(113, 100) + Fraction(25, 18) |
4 |
# returns Fraction(2267, 900)
|
5 |
|
6 |
Fraction(18, 5) / Fraction(18, 10) |
7 |
# returns Fraction(2, 1)
|
8 |
|
9 |
Fraction(18, 5) * Fraction(16, 19) |
10 |
# returns Fraction(288, 95)
|
11 |
|
12 |
Fraction(18, 5) * Fraction(15, 36) |
13 |
# returns Fraction(3, 2)
|
14 |
|
15 |
Fraction(12, 5) ** Fraction(12, 10) |
16 |
# returns 2.8592589556010197
|
El módulo también tiene algunos cuántos métodos importantes como limit_denominator(max_denominator)
el cuál encontrará y regresará una fracción más cercana en valor a la fracción dada cuyo denominador es a lo mucho max_denominator
. También puedes regresar el numerador de una fracción dada en el término más bajo usando la propiedad numerator
y el denominador usando la propiedad denominator
.
1 |
from fractions import Fraction |
2 |
|
3 |
Fraction('3.14159265358979323846') |
4 |
# returns Fraction(157079632679489661923, 50000000000000000000)
|
5 |
|
6 |
Fraction('3.14159265358979323846').limit_denominator(10000) |
7 |
# returns Fraction(355, 113)
|
8 |
|
9 |
Fraction('3.14159265358979323846').limit_denominator(100) |
10 |
# returns Fraction(311, 99)
|
11 |
|
12 |
Fraction('3.14159265358979323846').limit_denominator(10) |
13 |
# returns Fraction(22, 7)
|
14 |
|
15 |
Fraction(125, 50).numerator |
16 |
# returns 5
|
17 |
|
18 |
Fraction(125, 50).denominator |
19 |
# returns 2
|
También puedes usar este módulo con varias funciones en el módulo math para realizar cálculos basados en fracciones.
1 |
import math |
2 |
from fractions import Fraction |
3 |
|
4 |
math.sqrt(Fraction(25, 4)) |
5 |
# returns 2.5
|
6 |
|
7 |
math.sqrt(Fraction(28,3)) |
8 |
# returns 3.0550504633038935
|
9 |
|
10 |
math.floor(Fraction(3558, 1213)) |
11 |
# returns 2
|
12 |
|
13 |
Fraction(math.sin(math.pi/3)) |
14 |
# returns Fraction(3900231685776981, 4503599627370496)
|
15 |
|
16 |
Fraction(math.sin(math.pi/3)).limit_denominator(10) |
17 |
# returns Fraction(6, 7)
|
Ideas Finales
Estos dos módulos deberían ser suficientes para ayudarte a realizar operaciones comunes tanto con decimales como con fracciones. Como se muestra en la última sección, puedes usar estos módulos junto con el módulo de math para calcular el valor de todos los tipos de funciones matemáticas en el formato que desees.
En el siguiente tutorial en la serie, aprenderás acerca del módulo random en Python.