Skip to content

Latest commit

 

History

History
135 lines (89 loc) · 9.05 KB

File metadata and controls

135 lines (89 loc) · 9.05 KB
title
pytest / Assertion

pytest / Assertion

Exception ??

參考資料:

Assertion Introspection ??

驗證浮點數 {: #floating-point }

from pytest import approx

def test_simple_equal__not_working():
    assert not 1.0 / 7 == 0.142857142857

def test_almost_equal__approx():
    assert repr(approx(0.5, rel=1e-3)) == '0.5 +- 5.0e-04'
    assert repr(approx(0.5, abs=1e-3)) == '0.5 +- 1.0e-03'

    assert 1.0 / 7 == approx(0.142857142857) # rel=1e-6, abs=1e-12
    assert 1.0 / 7 == approx(0.1428, abs=1e-4) # 0.1427 ... 0.1429

def test_almost_equal__scale():
    assert int((1.0 / 7) * 10**4) == 1428 # 0.1428... x 10000 = 1428...

參考資料:

  • pytest.approx - Reference — pytest documentation

    • Assert that two numbers (or two sets of numbers) are equal to each other WITHIN SOME TOLERANCE.

      Due to the intricacies of floating-point arithmetic, numbers that we would intuitively expect to be equal are not always so:

      >>> 0.1 + 0.2 == 0.3
      False
      
    • This problem is commonly encountered when writing tests, e.g. when making sure that floating-point values are what you expect them to be. One way to deal with this problem is to assert that two floating-point numbers are equal to within some appropriate tolerance:

      >>> abs((0.1 + 0.2) - 0.3) < 1e-6
      True
      
    • However, comparisons like this are tedious to write and DIFFICULT TO UNDERSTAND. Furthermore, ABSOLUTE COMPARISONS like the one above are USUALLY DISCOURAGED because there’s no tolerance that works well for ALL SITUATIONS.

      1e-6 is good for numbers around 1, but too small for very big numbers and too big for very small ones. It’s better to express the tolerance as a FRACTION OF THE EXPECTED VALUE, but RELATIVE COMPARISONS like that are even more difficult to write correctly and concisely.

      上面這種 absolute comparison 其實不建議用 (寫起來煩是另一回事),因為 1e-6 對 1 可能剛好,但對比較大的數字而言又嫌太小,所以建議用 relative comparison (fraction of the expected value)。這概念還滿特別的,而且預設 1/1000000 對大部份情況已經夠精確,除非要比對的數字很大。

    • The approx class performs floating-point comparisons using a syntax that’s as INTUITIVE as possible:

      >>> from pytest import approx
      >>> 0.1 + 0.2 == approx(0.3)
      True
      

      The same syntax also works for sequences of numbers:

      >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6))
      True
      

      Dictionary values:

      >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6})
      True
      

      numpy arrays:

      >>> import numpy as np
      >>> np.array([0.1, 0.2]) + np.array([0.2, 0.4]) == approx(np.array([0.3, 0.6]))
      True
      

      And for a numpy array against a scalar:

      >>> import numpy as np
      >>> np.array([0.1, 0.2]) + np.array([0.2, 0.1]) == approx(0.3)
      True
      
    • By default, approx considers numbers within a RELATIVE TOLERANCE of 1e-6 (i.e. one part in a million) of its expected value to be equal.

      This treatment would lead to surprising results if the expected value was 0.0, because nothing but 0.0 itself is relatively close to 0.0. To handle this case less surprisingly, approx also considers numbers within an ABSOLUTE TOLERANCE of 1e-12 of its expected value to be equal.

    • Infinity and NaN are special cases. Infinity is only considered equal to itself, regardless of the relative tolerance. NaN is not considered equal to anything by default, but you can make it be equal to itself by setting the nan_ok argument to True. (This is meant to facilitate comparing arrays that use NaN to mean “no data”.)

      這是 NumPy 才有的東西 ??

    • Both the relative and absolute tolerances can be changed by passing arguments to the approx constructor:

      >>> 1.0001 == approx(1)
      False
      >>> 1.0001 == approx(1, rel=1e-3)
      True
      >>> 1.0001 == approx(1, abs=1e-3)
      True
      

      If you specify abs but not rel, the comparison will NOT consider the relative tolerance at all. In other words, two numbers that are within the default relative tolerance of 1e-6 will still be considered unequal if they exceed the specified absolute tolerance. If you specify both abs and rel, the numbers will be considered equal if EITHER tolerance is met:

      >>> 1 + 1e-8 == approx(1)
      True
      >>> 1 + 1e-8 == approx(1, abs=1e-12)
      False
      >>> 1 + 1e-8 == approx(1, rel=1e-6, abs=1e-12)
      True
      

      注意同時指定時,兩個條件的關係是 OR 不是 AND。另外 relative/absolute tolerance 預設值只有在 relabs 都沒有給的情況下才會有作用;大部份的情況下兩者都不用給。

  • python - pytest: assert almost equal - Stack Overflow

    • 如何驗證 float 的 "almost equal"? 甚至是在資料結構裡,例如 (1.32, 2.4)
    • dbn: pytest 3.0 開始提供 approx(),用法像是 assert 2.2 == pytest.approx(2.3) (不會過,因為預設的誤差是 1e-6) 或 assert 2.2 == pytest.approx(2.3, 0.1) (會過,因為 0.11e-1 是指容許下數後一位數的差異)

自訂 Assertion Comparison ??

參考資料

手冊: