1

For some reason it shows an error message: TypeError: argument should be a string or a Rational instance

import cmath
from fractions import Fraction

#Function
# Quadratic equatrion solver
def solver(a_entry, b_entry, c_entry):
    a = int(a_entry)
    b = int(b_entry)
    c = int(c_entry)
    d = (b*b) - (4*a*c)
    sol1 = (-b-cmath.sqrt(d)/(2*a))
    sol2 = (-b+cmath.sqrt(d)/(2*a))
    sol3 = Fraction(sol1)
    sol4 = Fraction(sol2)
    print(f"Value of x1 = {sol3} and value of x2 = {sol4}")

solver(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in solver
  File "/usr/lib/python3.10/fractions.py", line 139, in __new__
    raise TypeError("argument should be a string "
TypeError: argument should be a string or a Rational instance

I am a new programmer and I saw that this code generates a weird number (example: 5.42043240824+0j {inaccurate values}) when i give random values. So I want it to give either an accurate decimal values or in fraction. The fraction method dosen't work for some reason. Can someone please help. Alot of thanks.

Stef
  • 13,242
  • 2
  • 17
  • 28
  • You can set a higher precision for decimals if that would help, or round results but otherwise you are likely running into https://stackoverflow.com/questions/588004/is-floating-point-math-broken – JonSG Feb 13 '23 at 15:28
  • 2
    Are you intentionally using `cmath.sqrt` instead of `math.sqrt`? – Solomon Ucko Feb 13 '23 at 15:29
  • You can use `Fraction.limit_denominator(q)` to find the closest fraction with a denominator below q. For instance, `Fraction(0.333).limit_denominator(500)` becomes `Fraction(1, 3)` because 1/3 is the closest fraction to 0.333 with a denominator below 500. – Stef Feb 13 '23 at 15:53
  • 1
    Note that if your numbers are computed using a square root, there is no reason to expect a good fractional representation. The square root of an integer is either an integer, or an irrational number. Irrational means not a fraction. For instance, `sqrt(9)` is 3, which is integer, but `sqrt(2)` is around 1.414, and cannot be represented exactly as a fraction. – Stef Feb 13 '23 at 15:54
  • One possiblity would be to use module `sympy`, which won't replace the square roots by numerical approximations. For instance, `from sympy import sqrt; sol = (-3 - sqrt(18)) / (2*3); print(sol)` will print `-1/2 - sqrt(2)/2` – Stef Feb 13 '23 at 15:57
  • PS: When you post a question to stackoverflow, I encourage you to try to focus your question on your exact issue. Here you posted a relatively-long piece of code, most of which is completely irrelevant to your actual issue. The whole "times new roman" business is completely off-topic to your question, for instance. This discourages readers from actually reading your code and trying to understand your issue. – Stef Feb 13 '23 at 15:59
  • I took the liberty of editing your question to focus the issue and make the piece of code a [mre]. If you think that I misrepresented your question or if you dislike my edit for any reason, you can [rollback](https://stackoverflow.com/posts/75437902/revisions) my edit or [edit] your question further. – Stef Feb 13 '23 at 16:09

2 Answers2

0

2 things wrong in your code :

  • Use math instead of cmath as cmath is used for complexed values (it will always returns a complexe value, even 1+0j) which is not compatible with Fraction.
  • Be careful you wrote : (-b-cmath.sqrt(d)/(2*a)) but is should be ((-b-cmath.sqrt(d))/(2*a))

Also, the solution might no exist. For example, resolving 1x^2 + 3x + 10 has no answer (your fonction does not cross x axe). It still has complexe answer(s). To avoid this you can use a try except to catch errors. OR you can validate that d^2 is greater than 4ac because you can't sqrt negative values (except with complexe values ;) ) :

def solver():
  a = int(entry.get())
  b = int(entry1.get())
  c = int(entry2.get()) 
  d = (b*b) - (4*a*c)
  if d < 0:
    text = "no real answer ! The function doesn't cross X axe !"
    label2.configure(text = text)
  else:
    sol1 = ((-b-math.sqrt(d))/(2*a))
    sol2 = ((-b+math.sqrt(d))/(2*a))
    sol3 = Fraction(sol1)
    sol4 = Fraction(sol2)
    label2.configure(text = f"Value of x1 = {sol3} and value of x2 = {sol4}")

Hope it helps

SamRoch08
  • 121
  • 4
  • I'm guessing you mean "no real answer", not "no decimal answer". Square roots are only decimal when they are integer. – Stef Feb 13 '23 at 16:07
  • Also, what's the point of calling `Fraction` on a square root? What do you expect `Fraction(sqrt(2))` to return? – Stef Feb 13 '23 at 16:07
  • Indeed I edited my example with "no real answer", thanks ! Fraction will force a X/Y answer like 7/2, sqrt will return a value (float) like 3,5 – SamRoch08 Feb 13 '23 at 16:14
0

The issue with sqrt

It appears that you do not want to evaluate the square roots to numerical approximations. But that is exactly what cmath.sqrt and math.sqrt do: they calculate numerical approximations of square roots.

For instance:

import math

print( math.sqrt(2) )
# 1.4142135623730951

If you are not interested in numerical approximations, then I suggest using a library for symbolic calculus. The best-known library for symbolic calculus in python is called sympy. This module has a sympy.sqrt function that will simplify a square root as much as it can, but without returning a numerical approximation:

import sympy

print( sympy.sqrt(9) )
# 3

print( sympy.sqrt(2) )
# sqrt(2)

print( sympy.sqrt(18) )
# 3*sqrt(2)

More information about sympy: https://docs.sympy.org/latest/tutorials/intro-tutorial/intro.html

Other advice

When you write a program, it is most usually a good idea to cleanly separate the parts of the code that deal with algorithms, maths, and logic, from the parts of the code that deal with input and output. I suggest writing two functions, one that solves quadratic equations, and one that does input and output:

import sympy

# returns solutions of a x**2 + b x + c == 0
def solver(a, b, c):
    Delta = b*b - 4*a*c
    sol1 = (-b - sympy.sqrt(Delta)) / (2*a)
    sol2 = (-b + sympy.sqrt(Delta)) / (2*a)
    return (sol1, sol2)

# ask for user input and solve an equation
def input_equation_output_solution():
    a = int(entry.get())
    b = int(entry1.get())
    c = int(entry2.get())
    sol1, sol2 = solver(a, b, c)
    label2.configure(text = f"Value of x1 = {sol1} and value of x2 = {sol2}")
Stef
  • 13,242
  • 2
  • 17
  • 28