2012-11-30 11 views
5

Ho scritto una funzione denominata analyze_the_shape che accetta un elenco di vertici 2D in modo tale che l'elenco sia nell'ordine di una traversata in senso orario dei vertici nello spazio euclideo 2D.Perché ottengo ValueError: errore del dominio matematico?

Io lo chiamo nell'interprete e fornisco [(0, 0), (0, 4.0), (4.0, 4.0), (4.0, 0)] come input ma ottengo ValueError : math domain error. Mi aspetto di vedere return ["SQUARE", 4.0]. Cosa posso fare ?

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] 

Questo è l'errore che ottengo:

>>> 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 

risposta

1

Quando eseguo il codice, l'analisi dello stack che ottengo è:

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 

So math.acos accetta solo valori tali che -1.0 <= x <= 1.0 . Se stampo cos_angle < -1.0 subito prima della riga angle = math.degrees(math.acos(cos_angle)), stampa True. Se stampo cos_angle, stampa -1.0.

sto cercando di indovinare il problema qui è che i negozi Python modo cos_angle non è perfetto, e che il valore si genera per cos_angle è appena inferiore a -1.0.

Forse sarebbe meglio, se, invece di controllare abs(angle - 90.0) < 0.001, hai controllato se abs(cos_angle) < 0.001.

Edit:

Penso che tu abbia un errore in questa linea:

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

Probabilmente dovrebbe essere:

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

Nota le parentesi in più intorno (d3)**2 + (d4)**2. Questo assicura che l'aggiunta venga eseguita prima dello e che tale quantità venga sottratta da hypo**2.

+0

'abs (angolo - 90,0) <0,001' e' abs (cos_angle + 1) <0,001' non significano lo stesso. Se 'angle = 90',' cos_angle = 0'. Se 'cos_angle = -1',' angle = 180'. –

+0

Buona cattura! Penso che ci sia un errore in cui l'OP imposta 'cos_angle' - L'ho indicato nella mia risposta. –

+0

"il modo in cui Python memorizza' cos_angle' non è perfetto ". Questo dovrebbe "il modo in cui i numeri in virgola mobile sono rappresentati su un computer non è perfetto, e quindi gli errori di calcolo sono inevitabili". Python non ha assolutamente nulla a che fare con questo. – Bakuriu

8

Questa eccezione indica che cos_angle non è un parametro valido per math.acos.

In particolare, in questo esempio, è appena sotto -1, che è fuori dalla definizione acos.

Si potrebbe forse provare a forzare il tuo tornato cos_angle entro [-1,1] con qualcosa di simile:

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

Tuttavia, questo non tornerà SQUARE, dal momento che cos_angle è più o meno uguale a -1 nel tuo esempio, e angle quindi uguale a 180. Probabilmente c'è un problema con il tuo calcolo prima dell'eccezione.

0

Provare a arrotondare cos_angle. Ho avuto questo stesso problema; nel mio script il valore per x in math.acos(x) è uscito a -1,0000000000000002. Per risolvere il problema ho semplicemente arrotondato il valore di x a sei cifre decimali, quindi è uscito a -1,0.

2

Ho avuto lo stesso problema e risulta che @crld ha ragione. miei valori di ingresso dovrebbero essere nel range [-1, 1], ma ...

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

Quindi, come regola generale, suggerisco arrotondando tutti i carri che si alimenterà math.acos.

Problemi correlati