0

I am attempting to create a function in Python 3.11.4 to simplify radical expressions; the first step is to find the closest perfect square below the number. To do this, I tried making a function that decreases the number by 1 each time, checks if the root is an integer, prints if it is, and goes to the next lowest number if not.

import math


def closest_perfect_square(radical):
    while True:
        rad_root = math.sqrt(radical)
        if isinstance(rad_root, int):
            print(rad_root)
        else:
            radical -= 1


closest_perfect_square(60)

Each time I run this program using any radical, it gives the ValueError: math domain error. It would be much appreciated if anyone could help me figure out why it keeps giving me this error.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
  • 1
    You are decreasing `radical` until it becomes negative and then `math.sqrt` will fail. I don't understand how you intended this to work. – mkrieger1 Aug 30 '23 at 22:06
  • 1
    Also if the idea is to get the perfect square below your input, why aren't you just taking the square root of that input and then rounding that down? No while loop required. – Mike 'Pomax' Kamermans Aug 30 '23 at 22:18
  • 2
    `isinstance(rad_root, int)` will never be true because `math.sqrt` always returns a `float`. You want `rad_root.is_integer()`. – wjandrea Aug 30 '23 at 22:23
  • 3
    Related: [Integer square root in python](/q/15390807/4518341). I think you want `math.isqrt`. – wjandrea Aug 30 '23 at 22:25
  • Possible duplicate: [How do I calculate square root in Python?](/q/70793490/4518341) The canonical answer covers this error, but I wrote it so I might be biased. – wjandrea Aug 30 '23 at 22:26

2 Answers2

0

Utilizing the good comments above and adding one more needed bit of code, following is a refactored version of your program.

import math

def closest_perfect_square(radical):
    while True and radical >= 0:        # As noted in the comments about negative numbers
        rad_root = math.sqrt(radical)
        if rad_root.is_integer():       # As noted in the comments about integer determination
            print(rad_root)
            break                       # Need this or otherwise the while loop will continue indefinitely 
        else:
            radical -= 1

closest_perfect_square(60)

The additional thing to note is the need for the "break" statement. Otherwise, with the other tweaks, the program will continue to print out the closest root value indefinitely.

Testing out this refactored version yields the following terminal output.

craig@Vera:~/Python_Programs/Radical$ python3 Radical.py 
7.0

Give that a go.

NoDakker
  • 3,390
  • 1
  • 10
  • 11
0

Corrected, seeing that what is desired is the perfect square and not the square root of that perfect square. Following @wjandrea's pointer to math.isqrt, what you want is

import math

def closest_perfect_square(radical):
    return math.isqrt(radical)**2

Which is roughly equivalent to

def closest_perfect_square(radical):
    return math.floor(math.sqrt(radical))**2

In any event there is no need to iterate down the integers to find the perfect square. If sqrt(radical) yields n + f where n is an integer and f is a fraction less than one, then n is the square root of the largest perfect square less than radical.

Malcolm
  • 461
  • 2
  • 10