Как я могу получить точный результат 10 ** 20 + 10 ** - 20 в Python? Это дает мне 1e + 20 - PullRequest
1 голос
/ 01 октября 2019

Я пишу код для решения уравнений второго класса, и он работает просто отлично. Однако при вводе следующего уравнения:

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

(Да, мой ввод 10 ** 20 + 10 ** (- 20)
Я получаю:

x 1 = 0
x 2 = -1e + 20

Тем не менее, если (10 ^ (20) + 10 ^ (- 20)) принять 10e + 20, то, если вы выполните математику:

Вот формула в формате LaTeX:

LaTeX formatted formula

Что составляет почти 10 ^ 20, но не 10 ^ 20.
Как я могу получить точный результат этой операции, чтобы я мог получить точное значение уравнения в x 2 ?

Мой код следующий:

#===============================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()

Не обращайте внимания на комментарии, я из испаноязычной страны.

1 Ответ

3 голосов
/ 01 октября 2019

Ограничения типа с плавающей запятой для машины являются причиной, по которой при добавлении очень маленького числа к очень большому числу маленькое число просто игнорируется.

Это явление называется поглощением или отменой.

С помощью пользовательских объектов с плавающей точкой (например, модуля decimal) вы можете добиться любой точности (вычисления выполняются медленнее, потому что теперь с плавающей точкой эмулируется и больше не зависит от возможностей FPU машины). )

Из десятичного модуля документы :

В отличие от двоичной плавающей запятой, основанной на аппаратном обеспечении, десятичный модуль имеет изменяемую пользователем точность (по умолчанию 28 мест), котораяможет быть настолько большим, насколько необходимо для данной проблемы

Этого можно достичь, изменив следующий глобальный параметр 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)

сейчас

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

Как указано в комментариях, для небольшого числа безопаснее позволить decimal модулю обрабатывать деление с помощью оператора power на уже существующем Decimal object (даже если здесь, результат тот же):

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

Таким образом, вы можете использовать decimal.Decimal объекты для своих вычислений вместо собственных float.

...