7

I wrote a function named analyze_the_shape that takes a list of 2D vertices such that the list is in the order of a clockwise traversal of the vertices in the 2D Euclidean space.

I call it in the interpreter and give [(0, 0), (0, 4.0), (4.0, 4.0), (4.0, 0)] as input but I get ValueError : math domain error. I expect to see return ["SQUARE", 4.0]. What can I do ?

import math

def analyze_the_shape(liste):
    if len(liste) == 2 :
        d = ( (liste[1][0] - liste[0][0])**2 + (liste[1][1] - liste[0][1])**2 )**(0.5)   
        return ["LINESEGMENT", d ] 
    if len(liste) == 4 :
        d1 = abs(( (liste[1][0] - liste[0][0])**2 + (liste[1][1] - liste[0][1])**2 )**(0.5))
        d2 = abs(( (liste[2][0] - liste[1][0])**2 + (liste[2][1] - liste[1][1])**2 )**(0.5))
        d3 = abs(( (liste[3][0] - liste[2][0])**2 + (liste[3][1] - liste[2][1])**2 )**(0.5))
        d4 = abs(( (liste[0][0] - liste[3][0])**2 + (liste[0][1] - liste[3][1])**2 )**(0.5)) 
        hypo = abs(( (liste[2][1] - liste[0][1])**2 + (liste[2][0] - liste[0][0])**2 )**(0.5))
        cos_angle = float((hypo**2 - (d3)**2 + (d4)**2) / ((-2.0)*(d4)*(d3)))
        angle = math.degrees(math.acos(cos_angle))
        if d1 == d2 == d3 == d4 and abs(angle - 90.0) < 0.001 :
            return ["SQUARE", d1]  

This is the error I get:

>>> import a
>>> a.analyze_the_shape([(0, 0), (0, 4.0), (4.0, 4.0), (4.0, 0)])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "a.py", line 15, in analyze_the_shape

ValueError: math domain error
Sam Mussmann
  • 5,883
  • 2
  • 29
  • 43
Ali Candan
  • 71
  • 1
  • 1
  • 3

4 Answers4

9

This exception means that cos_angle is not a valid parameter for math.acos.

Specifically, in this example, it is just below -1, which is out of acos definition.

You could probably try to force your returned cos_angle within [-1,1] with something like :

def clean_cos(cos_angle):
    return min(1,max(cos_angle,-1))

However, this will not return SQUARE, since cos_angle is more or less equals to -1 in your example, and angle thus equals 180. There is probably an issue with your computation before the exception.

Julien Vivenot
  • 2,230
  • 13
  • 17
3

I had the same problem and it turns out @crld is right. My input values are supposed to be in the range [-1, 1], but...

print('{0:.32f}'.format(x))
>> 1.00000000000000022204460492503131

So, as a general rule, I suggest rounding all the floats you feed into math.acos.

Procope
  • 91
  • 1
  • 7
1

When I run your code, the stack trace I get is:

Traceback (most recent call last):
  File "md.py", line 22, in <module>
    analyze_the_shape([(0, 0), (0, 4.0), (4.0, 4.0), (4.0, 0)])
  File "md.py", line 18, in analyze_the_shape
    angle = math.degrees(math.acos(cos_angle))
ValueError: math domain error

I know math.acos only accepts values such that -1.0 <= x <= 1.0. If I print out cos_angle < -1.0 right before the line angle = math.degrees(math.acos(cos_angle)), it prints True. If I print out cos_angle, it prints -1.0.

I'm guessing the issue here is that the way Python stores cos_angle isn't perfect, and that the value you generate for cos_angle is just barely less than -1.0.

Perhaps it would be better, if, instead of checking abs(angle - 90.0) < 0.001, you checked if abs(cos_angle) < 0.001.

Edit:

I think you have an error in this line:

cos_angle = float((hypo**2 - (d3)**2 + (d4)**2) / ((-2.0)*(d4)*(d3)))

It probably should be:

cos_angle = float((hypo**2 - ((d3)**2 + (d4)**2)) / ((-2.0)*(d4)*(d3)))

Note the extra parens around (d3)**2 + (d4)**2. This makes sure that addition is done before you subtract that quantity from hypo**2.

Sam Mussmann
  • 5,883
  • 2
  • 29
  • 43
  • `abs(angle - 90.0) < 0.001` and `abs(cos_angle + 1) < 0.001` do not mean the same. If `angle=90`, `cos_angle=0`. If `cos_angle=-1`, `angle=180`. – Julien Vivenot Nov 30 '12 at 01:11
  • Good catch! I think there's an error where the OP sets `cos_angle` -- I've pointed that out in my answer. – Sam Mussmann Nov 30 '12 at 01:16
  • "the way Python stores `cos_angle` isn't perfect". This should the "the way floating point numbers are represented on a computer is not perfect, and thus computational errors are unavoidable". Python has absolutely nothing to do with this. – Bakuriu Nov 30 '12 at 09:18
1

Try rounding cos_angle. I had this same problem; in my script the value for x in math.acos(x) came out to -1.0000000000000002. To fix it I just rounded the value for x to six decimal places so it came out to -1.0.

crld
  • 387
  • 3
  • 9