为什么我会得到“ValueError: math domain error”错误?

7
我写了一个名为analyze_the_shape的函数,它接受一个顺时针遍历2D欧几里得空间中顶点的列表作为输入。
我在解释器中调用它,并给出[(0, 0), (0, 4.0), (4.0, 4.0), (4.0, 0)]作为输入,但我得到了ValueError: math domain error的错误信息。我期望看到return ["SQUARE", 4.0]。我该怎么办?
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]  

我收到的错误信息如下:

>>> 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
4个回答

9
这个异常意味着cos_angle不是math.acos的有效参数。

具体来说,在这个例子中,它略低于-1,超出了acos的定义范围。

你可以尝试使用类似以下代码来强制将返回的cos_angle限制在[-1,1]之间:

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

然而,这不会返回SQUARE,因为在你的例子中,cos_angle或多或少等于-1,因此angle等于180。在异常之前,你的计算可能存在问题。


3

我曾经遇到过同样的问题,@crld说得对。 我的输入值应该在[-1, 1]范围内,但是...

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

所以,作为一个通用规则,我建议将你传入 math.acos 的所有浮点数四舍五入。


1

当我运行你的代码时,我得到的堆栈跟踪是:

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

我知道math.acos只接受-1.0 <= x <= 1.0的值。如果我在angle = math.degrees(math.acos(cos_angle))这行代码之前打印出cos_angle < -1.0,它会输出True。如果我打印出cos_angle,它会输出-1.0

我猜问题在于Python存储cos_angle的方式不完美,而你生成的值仅比-1.0略小。

也许更好的方法是,不要检查abs(angle - 90.0) < 0.001,而是检查abs(cos_angle) < 0.001

编辑:

我认为这一行代码有误:

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

可能应该是这样:

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

请注意在(d3)**2 + (d4)**2周围的额外括号。这确保了在从hypo**2中减去该数量之前进行加法运算。

“abs(angle - 90.0) < 0.001”和“abs(cos_angle + 1) < 0.001”并不相同。如果angle为90,则cos_angle为0。如果cos_angle为-1,则angle为180。 - Julien Vivenot
很好的发现!我认为在OP设置cos_angle时有一个错误——我在我的答案中指出了这一点。 - Sam Mussmann
Python存储cos_angle的方式并不完美。这意味着计算机上浮点数的表示并不完美,因此计算误差是不可避免的。Python与此毫无关系。 - Bakuriu

1

尝试四舍五入cos_angle。我遇到了同样的问题;在我的脚本中,math.acos(x) 中x的值为-1.0000000000000002。为了解决这个问题,我只需将x的值四舍五入到小数点后六位,这样它就变成了-1.0。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接