1

I am writing a code to solve second-grade equations and it works just well. However, when I input the following equation:

x^2 + (10^(20) + 10^(-20)) + 1 = 0

(Yes, my input is 10**20 + 10**(-20)
I get:

x1 = 0
x2 = -1e+20

However, it is taking (10^(20) + 10^(-20) as 10e+20 while, if you do the math:

Here is the LaTeX formatted formula:

LaTeX formatted formula

Which is almost 10^20 but not 10^20.
How can I get the exact result of that operation so I can get the exact value of the equation in x2?

My code is the following:

#===============================Función para obtener los coeficientes===============================

#Se van a anidar dos funciones. Primero la de coeficientes, luego la de la solución de la ecuación.
#Se define una función recursiva para obtener los coeficientes de la ecuación del usuario
def cof():
  #Se determina si el coeficiente a introducir es un número o una cadena de operaciones
  op = input("Si tu coeficiente es un número introduce 1, si es una cadena de operaciones introduce 2")
  #Se compara la entrada del usuario con la opción.
  if op == str(1):
    #Se le solicita el número
    num = input("¿Cuál es tu número?")
    #Se comprueba que efectívamente sea un número
    try:
      #Si la entrada se puede convertir a flotante
      float(num)
      #Se establece el coeficiente como el valor flotante de la entrada
      coef = float(num)
      #Se retorna el valor del coeficiente
      return coef
    #Si no se pudo convertir a flotante...
    except ValueError:
      #Se le informa al usuario del error
      print("No introdujiste un número. Inténtalo de nuevo")
      #Se llama a la función de nuevo
      return cofa()
  #Si el coeficiente es una cadena (como en 10**20 + 10**-20)
  elif op == str(2):
    #La entrada se establece como la entrada del usuario
    entrada = input("Input")
    #Se intenta...
    try:
      #Evaluar la entrada. Si se puede...
      eval(entrada)
      #El coeficiente se establece como la evaluación de la entrada
      coef = eval(entrada)
      #Se regresa el coeficiente
      return coef
    #Si no se pudo establecer como tal...
    except:
      #Se le informa al usuario
      print("No introdujiste una cadena de operaciones válida. Inténtalo de nuevo")
      #Se llama a la función de nuevo
      return cofa()
  #Si no se introdujo ni 1 ni 2 se le informa al usuario
  else:
    #Se imprime el mensaje
    print("No introdujiste n ni c, inténtalo de nuevo")
    #Se llama a la función de nuevo
    return cof()

#===============================Función para resolver la ecuación===============================
#Resuelve la ecuación
def sol_cuadratica():

  #Se pide el valor de a
  print("Introduce el coeficiente para a")
  #Se llama a cof y se guarda el valor para a
  a = cof()
  #Se pide b
  print("Introduce el coeficiente para b")
  #Se llama cof y se guarda b
  b = cof()
  #Se pide c
  print("Introduce el coeficiente para c")
  #Se llama cof y se guarda c
  c = cof()
  #Se le informa al usuario de la ecuación a resolver
  print("Vamos a encontrar las raices de la ecuación {}x² + {}x + {} = 0".format(a, b, c))
  #Se analiza el discriminante
  discriminante = (b**2 - 4*a*c)
  #Si el discriminante es menor que cero, las raices son complejas
  if discriminante < 0:
    #Se le informa al usuario
    print("Las raices son imaginarias. Prueba con otros coeficientes.")
    #Se llama a la función de nuevo
    return sol_cuadratica()
  #Si el discriminante es 0, o mayor que cero, se procede a resolver
  else:   
    #Ecuación para x1
    x1 = (-b + discriminante**(1/2))/(2*a)
    #Ecuación para x2
    x2 = (-b - discriminante**(1/2))/(2*a)
    #Se imprimen los resultados
    print("X1 = " + str(x1))
    print("X2 = " + str(x2))

sol_cuadratica()

Ignore the comments, I'm from a Spanish-speaking country.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • How many significant figures (not decimal places) does your implementation of python display? – cup Oct 01 '19 at 17:19
  • floating point absorption/cancellation is the reason. – Jean-François Fabre Oct 01 '19 at 17:20
  • 1
    Related: [Clarification on the Decimal type in Python](https://stackoverflow.com/q/20354423/555045) – harold Oct 01 '19 at 17:21
  • 1
    It's most likely due to the inherent limitations of doing float-point arithmetic on computers. I suggest you install the [mpmath](http://mpmath.org/) module to overcome the issue. You can get it on [pypi](https://pypi.org/project/mpmath/). – martineau Oct 01 '19 at 17:22
  • @Igor: I suggest you put the code through [Google Translate](https://translate.google.com) — it often does an excellent job (even on code). – martineau Oct 01 '19 at 17:29

1 Answers1

3

The limitations of the machine floating point type is the reason why when adding a very small number to a very big number, the small number is just ignored.

This phenomenon is called absorption or cancellation.

With custom floating point objects (like the ones decimal module) you can achieve any precision (computations are slower, because floating point is now emulated, and not relying on the machine FPU capabilities anymore)

From the decimal module docs:

Unlike hardware based binary floating point, the decimal module has a user alterable precision (defaulting to 28 places) which can be as large as needed for a given problem

This can be achieved by changing the following global parameter decimal.getcontext().prec

import decimal
decimal.getcontext().prec = 41  # min value for the required range

d = decimal.Decimal(10**20)
d2 = decimal.Decimal(10**-20)

now

>>> d+d2
Decimal('100000000000000000000.00000000000000000001')

As suggested in comments, for the small number it's safer to let decimal module handle the division by using power operator on an already existing Decimal object (even if here, the result is the same):

d2 = decimal.Decimal(10)**-20

So you can use decimal.Decimal objects for your computations instead of native floats.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219