あなたの例では、計算時に丸め誤差が発生しますdot(u,v)/(norm(u)*norm(v))
。テスト値の場合、計算は事実上2/(sqrt(2)*sqrt(2))
. sqrt(2) の計算値は、無限精度値よりわずかに大きい値に丸められます。
>>> math.sqrt(2)
1.4142135623730951
>>> math.sqrt(2)*math.sqrt(2)
2.0000000000000004
>>> 2/(math.sqrt(2)*math.sqrt(2))
0.9999999999999998
>>> math.acos(2/(math.sqrt(2)*math.sqrt(2)))
2.1073424255447017e-08
@FJdecimal
によるモジュール ソリューションは2/(sqrt(2)*sqrt(2))
、より高い精度で計算されます。この値が (arccos によって) float に変換されると、1.0 に丸められます。
>>> import decimal
>>> decimal.getcontext().sqrt(2)
Decimal('1.414213562373095048801688724')
>>> decimal.getcontext().sqrt(2)**2
Decimal('1.999999999999999999999999999')
>>> 2/decimal.getcontext().sqrt(2)**2
Decimal('1.000000000000000000000000001')
>>> float(2/decimal.getcontext().sqrt(2)**2)
1.0
と異なる精度2/(sqrt(2)*sqrt(2))
を使用して計算すると、別の問題が浮き彫りになります。decimal
>>> for i in range(10,30):
... decimal.getcontext().prec=i
... print i,2/decimal.getcontext().sqrt(2)**2
...
10 1.000000001
11 0.99999999995
12 1.00000000001
13 1
14 1
15 0.999999999999995
16 1
17 1.0000000000000001
18 1
19 0.9999999999999999995
20 1
21 1
22 0.9999999999999999999995
23 1
24 1
25 0.9999999999999999999999995
26 1.0000000000000000000000001
27 1.00000000000000000000000001
28 1.000000000000000000000000001
29 1
結果は、正確に 1、1 未満、または 1 より大きい場合がありますarccos
。最初に浮動小数点数に丸めずに取得できる場合、これは混乱を招く可能性があります。1 より大きい値の場合、arccos
は定義されていないため、結果は になりnan
ます。このタイプの丸め誤差が見られる場合、中間値が 1 を超えたときに緯度/経度の計算が中断されます。すべての計算の精度を上げただけでは (たとえば、float64 から float128 に)、問題は解決しません。問題が別の値のセットに移動する可能性がありますが、丸め誤差は引き続き発生します。
更新 1
他にもいくつかのオプションがあります。式を次のように書き換えることができます。
def AcuteAngle3(line1,line2):
''':: line(x1,y1,x2,y2)'''
u = (line1[2]-line1[0], line1[3]-line1[1])
v = (line2[2]-line2[0], line2[3]-line2[1])
return arccos(sqrt(abs(dot(u,v)**2/(dot(u,u)*dot(v,v)))))
AcuteAngle3
元の問題を回避できますdot(u,u)*dot(v,v)
が、実際の値よりも大きさがわずかに小さい値に丸められる可能性があり、1 より大きい値のアークコスを取得しようとする可能性があります (ただし、式全体に ROUND_UP または ROUND_DOWN を使用するだけです)。私のdecimal
例では、さまざまな丸めモードを使用してみましたが、いくつかの丸め「エラー」が残っていました。)
次の関数は、これらの例外的な発生をチェックします。
def AcuteAngle4(line1,line2):
''':: line(x1,y1,x2,y2)'''
u = (line1[2]-line1[0], line1[3]-line1[1])
v = (line2[2]-line2[0], line2[3]-line2[1])
temp = sqrt(abs(dot(u,v)**2/(dot(u,u)*dot(v,v))))
if temp > 1:
return 0.0
else:
return arccos(temp)
中間値をより高い精度で計算してから切り捨てる、または式の各コンポーネントを計算するときに 0 に近づくか遠ざかる方向に選択的に丸めることは、他の可能性です。