web-dev-qa-db-ja.com

n個の有効10進数に丸められたときに2つの数値がほぼ等しいかどうかを判別する関数

サードパーティから提供されたライブラリをテストするように依頼されました。ライブラリは、nの有効数字に対して正確であることがわかっています。重要度の低いエラーは無視しても問題ありません。結果を比較するのに役立つ関数を書きたい:

def nearlyequal( a, b, sigfig=5 ):

この関数の目的は、2つの浮動小数点数(aとb)がほぼ等しいかどうかを判別することです。この関数は、a == b(完全一致)の場合、または10進数で記述したときにsigfig有効数字に丸めたときにaとbの値が同じ場合にTrueを返します。

誰かが良い実装を提案できますか?私はミニユニットテストを書きました。あなたが私のテストでバグを見ることができない限り、良い実装は以下をパスするはずです:

assert nearlyequal(1, 1, 5) 
assert nearlyequal(1.0, 1.0, 5) 
assert nearlyequal(1.0, 1.0, 5) 
assert nearlyequal(-1e-9, 1e-9, 5) 
assert nearlyequal(1e9, 1e9 + 1 , 5) 
assert not nearlyequal( 1e4, 1e4 + 1, 5) 
assert nearlyequal( 0.0, 1e-15, 5 ) 
assert not nearlyequal( 0.0, 1e-4, 6 ) 

その他の注意事項:

  1. 値aおよびbのタイプは、int、float、またはnumpy.float64です。値aとbは常に同じタイプになります。変換によって関数に追加のエラーが発生しないことが重要です。
  2. この数値を保持してみましょう。文字列に変換する関数や非数学的なトリックを使用する関数は理想的ではありません。このプログラムは、関数が想定どおりに機能することを証明できるようにする必要がある数学者である誰かによって監査されます。
  3. 速度...多くの数値を比較しなければならないので、速度が速いほど良いです。
  4. Numpy、scipy、および標準ライブラリがあります。それ以外のものは、特にプロジェクトのごく一部については、入手するのが難しいでしょう。
41
Salim Fadhley

関数assert_approx_equal in numpy.testing(source here) これは良い出発点になるかもしれません。

def assert_approx_equal(actual,desired,significant=7,err_msg='',verbose=True):
    """
    Raise an assertion if two items are not equal up to significant digits.

    .. note:: It is recommended to use one of `assert_allclose`,
              `assert_array_almost_equal_nulp` or `assert_array_max_ulp`
              instead of this function for more consistent floating point
              comparisons.

    Given two numbers, check that they are approximately equal.
    Approximately equal is defined as the number of significant digits
    that agree.
23
dF.

Python 3.5以降、これを行う標準的な方法(標準ライブラリを使用)は math.isclose 関数。

次の署名があります。

isclose(a, b, rel_tol=1e-9, abs_tol=0.0)

絶対誤差許容度の使用例:

from math import isclose
a = 1.0
b = 1.00000001
assert isclose(a, b, abs_tol=1e-8)

nの有効桁数で精度を上げるには、最後の行を次のように置き換えます。

assert isclose(a, b, abs_tol=10**-n)
74
erb

これがテイクです。

def nearly_equal(a,b,sig_fig=5):
    return ( a==b or 
             int(a*10**sig_fig) == int(b*10**sig_fig)
           )
7
Triptych

私はあなたの質問が十分に定義されていないと思います、そしてあなたが提示するユニットテストはそれを証明します:

「小数点以下N桁に丸める」とは、「小数点以下N桁」を意味する場合、テストassert nearlyequal(1e9, 1e9 + 1 , 5)は失敗します。これは、1000000000と1000000001を0.00001に丸めた場合でも正確さ、それらはまだ異なります。

また、「小数点以下N桁に丸める」という意味の場合、「小数点に関係なく最上位N桁」を意味します。0.000000001と-0.000000001はまったく異なるため、テストassert nearlyequal(-1e-9, 1e-9, 5)は失敗します。このように見たとき。

最初の定義を意味する場合は、このページの最初の答え(トリプティクによる)が適切です。あなたが2番目の定義を意味するなら、それを言ってください、私はそれについて考えることを約束します:-)

6
Oren Shemesh

すでにすばらしい答えはたくさんありますが、ここでは考えを示します。

def closeness(a, b):
  """Returns measure of equality (for two floats), in unit
     of decimal significant figures."""
  if a == b:
    return float("infinity")
  difference = abs(a - b)
  avg = (a + b)/2
  return math.log10( avg / difference )


if closeness(1000, 1000.1) > 3:
  print "Joy!"

10進数の「有効数字」とは、小数点を調整し、整数に切り捨てることです。

>>> int(3.1415926 * 10**3)
3141
>>> int(1234567 * 10**-3)
1234
>>>
2
S.Lott

これは、浮動小数点数に関するかなり一般的な問題です。 Demmel [1]のセクション1.5での議論に基づいて解決します。 (1)丸め誤差を計算します。 (2)丸め誤差がイプシロン以下であることを確認します。 pythonはしばらく使用しておらず、バージョン2.4.3しかありませんが、これを正しく取得するように努めます。

ステップ1.丸め誤差

def roundoff_error(exact, approximate):
    return abs(approximate/exact - 1.0)

ステップ2.浮動小数点の等価性

def float_equal(float1, float2, epsilon=2.0e-9):
    return (roundoff_error(float1, float2) < epsilon)

このコードには明らかにいくつかの欠点があります。

  1. 正確な値がゼロの場合、ゼロ除算エラー。
  2. 引数が浮動小数点値であることを確認しません。

改訂1。

def roundoff_error(exact, approximate):
    if (exact == 0.0 or approximate == 0.0):
        return abs(exact + approximate)
    else:
        return abs(approximate/exact - 1.0)

def float_equal(float1, float2, epsilon=2.0e-9):
    if not isinstance(float1,float):
        raise TypeError,"First argument is not a float."
    Elif not isinstance(float2,float):
        raise TypeError,"Second argument is not a float."
    else:
        return (roundoff_error(float1, float2) < epsilon)

それは少し良いです。正確な値または近似値のいずれかがゼロの場合、エラーは他方の値と等しくなります。浮動小数点値以外のものが提供された場合、TypeErrorが発生します。

この時点で、イプシロンの正しい値を設定するのが唯一の難しいことです。バージョン2.6.1のドキュメントでsys.float_infoにepsilon属性があることに気付いたので、その値の2倍をデフォルトのイプシロンとして使用します。ただし、正しい値は、アプリケーションとアルゴリズムの両方に依存します。

[1] James W. Demmel、Applied Numerical Linear Algebra、SIAM、1997。

2
tmh

Oren Shemeshは、述べられているように問題の一部を取りましたが、それだけではありません。

ほぼ等しい(0.0、1e-15、5)を主張する

また、2番目の定義も失敗します(それが私が学校で学んだ定義です)。

何桁見ても、0はゼロ以外の値にはなりません。正解がゼロのケースがある場合、これはそのようなテストの頭痛の種になる可能性があります。

1
Loren Pechtel

"Comploing Floating Point Numbers" に、B。Dawson(C++コードを使用)による興味深い解決策があります。彼のアプローチは、2つの数値の厳密なIEEE表現と、それらの数値が符号なし整数として表される場合の強制的な辞書式順序に依存しています。

1
Dan M.

サードパーティから提供されたライブラリをテストするように依頼されました

デフォルトを使用している場合Python unittest framework 、使用できます assertAlmostEqual

self.assertAlmostEqual(a, b, places=5)
0
serv-inc

2つの数値を比較して、有効数字Nに一致するかどうかを確認する方法はたくさんあります。大まかに言えば、比較する2つの数値の最大値の10 ^ -N倍未満であることを確認したいだけです。とても簡単です。

しかし、数値の1つがゼロの場合はどうなりますか?ゼロと比較すると、相対差または有効数字の概念全体が落ちます。そのような場合に対処するには、絶対差分も必要です。絶対差分は、相対差分とは別に指定する必要があります。

このブログ投稿で、浮動小数点数を比較する際の問題(ゼロを処理する特定のケースを含む)について説明します。

http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/

0
Bruce Dawson