断言浮点值与有效数字容差相等

问题描述 投票:0回答:5

我正在尝试编写一个单元测试来检查工程分析的输出。我有理论值,我想根据对一定数量的有效数字的分析来检查这些值。例如:

Ixx_ther = 0.000123
Iyy_ther = 0.0123

Ixx, Iyy = getI(*args, **kwargs)

self.assertAlmostEqual(Ixx_ther, Ixx, 6)
self.assertAlmostEqual(Iyy_ther, Iyy, 4)

在这种情况下,我需要知道我要检查的数字,因为在两种情况下将容差设置为 6 都会使测试过于严格,而将其设置为 4 则会过于宽松。我需要的是测试是否等于相同数量的有效数字。理想的说法是:

Ixx_ther = 1.23E-4
Iyy_ther = 1.23E-2

Ixx, Iyy = getI(*args, **kwargs)

self.assertAlmostEqual(Ixx_ther, Ixx, 2)
self.assertAlmostEqual(Iyy_ther, Iyy, 2)

并让断言语句删除指数并仅检查有效数是否相等。我想这以前已经做过了,但我一直无法找到一个内置函数来以这种方式断言相等。有没有人遇到过这个问题,

问题

1)是否有人以前遇到过这个问题,并且了解工程分析单元测试的一般指南

2)是否有内置的解决方案。对于这个问题

3)是否有人已经编写了一个以这种方式工作的自定义断言语句?

python unit-testing assert scientific-computing
5个回答
6
投票

回复:是否有内置解决方案:如果您可以将

numpy
作为依赖项,请查看 numpy.testing.

这是一个示例(逐字来自

assert_allclose
文档):

>>> x = [1e-5, 1e-3, 1e-1]
>>> y = np.arccos(np.cos(x))
>>> assert_allclose(x, y, rtol=1e-5, atol=0)

编辑:为了完整起见,这里是源代码的链接:

assert_allclose
将实际工作转发到np.allclose。这与 @Mark Ransom 的答案几乎相同(加上数组参数和无穷大的处理)。


4
投票

这是我在另一个问题上留下的答案的重写。

def AlmostEqual(a, b, digits): epsilon = 10 ** -digits return abs(a/b - 1) < epsilon

如果

b

 可以为零,这需要更多的工作。


0
投票
也许没有回答您问题的全部范围,但这就是我编写这样一个函数的方式:

def assertAlmostEqual(arg1,arg2,tolerance=2): str_formatter = '{0:.' + str(tolerance) + 'e}' lhs = str_formatter.format(arg1).split('e')[0] rhs = str_formatter.format(arg2).split('e')[0] assert lhs == rhs

Python 的

字符串格式化迷你语言可用于将浮点数格式化为给定方式。因此,我们能做的就是强制将它们格式化为指数表示法,这样,对于输入 0.123

0.000123
,我们有:

str_formatter.format(0.123) == '1.23e-01' str_formatter.format(0.000123) == '1.23e-04'

剩下的就是去掉指数并断言相等。

演示:

assertAlmostEqual(0.0123,0.0001234) assertAlmostEqual(0.123,0.0001234) assertAlmostEqual(0.123,0.0001234,tolerance=3) --------------------------------------------------------------------------- AssertionError Traceback (most recent call last) /home/xxx/<ipython-input-83-02fbd71b2e87> in <module>() ----> 1 assertAlmostEqual(0.123,0.0001234,tolerance=3) /home/xxx/<ipython-input-74-ae32ed74769d> in assertAlmostEqual(arg1, arg2, tolerance) 3 lhs = str_formatter.format(arg1).split('e')[0] 4 rhs = str_formatter.format(arg2).split('e')[0] ----> 5 assert lhs == rhs 6 AssertionError:

如果您不喜欢我定义的方式,可能会出现相差一的问题

tolerance

。不过,明白了这个想法。


0
投票
感谢

roippi

的好主意,我稍微修改了你的代码:

def assertAlmostEqualSigFig(self, arg1,arg2,tolerance=2): if tolerance > 1: tolerance -= 1 #end str_formatter = '{0:.' + str(tolerance) + 'e}' significand_1 = float(str_formatter.format(arg1).split('e')[0]) significand_2 = float(str_formatter.format(arg2).split('e')[0]) exponent_1 = int(str_formatter.format(arg1).split('e')[1]) exponent_2 = int(str_formatter.format(arg2).split('e')[1]) self.assertEqual(significand_1, significand_2) self.assertEqual(exponent_1, exponent_2) return

我改变了一些事情

1)我检查指数和尾数(那是最上面的单词不是吗)

2)我将有效数和指数分别转换为 float / int。这可能不是必要的,但我更愿意检查数字作为数字而不是字符串的相等性。

3)

Jim Lewis

指出我需要将容差调整一,因为 0.0123 的正确格式字符串 {0:.3e} 是 1.230E-2 而不是 0.123E-1。即,如果您想要三位有效数字,则只需要小数点后两位数字,因为小数点之前的数字也很重要。

她是一个实施示例

class testSigFigs(Parent_test_class): @unittest.expectedFailure def test_unequal_same_exp(self): self.assertAlmostEqualSigFig(0.123, 0.321, 3) @unittest.expectedFailure def test_unequal_diff_exp(self): self.assertAlmostEqualSigFig(0.123, 0.0321, 3) @unittest.expectedFailure def test_equal_diff_exp(self): self.assertAlmostEqualSigFig(0.0123, 0.123, 3) def test_equal_same_exp(self): self.assertAlmostEqualSigFig(0.123, 0.123, 3) def test_equal_within_tolerance(self): self.assertAlmostEqualSigFig(0.123, 0.124, 2) #end

输出:

test_equal_diff_exp (__main__.testSigFigs) ... expected failure test_equal_same_exp (__main__.testSigFigs) ... ok test_equal_within_tolerance (__main__.testSigFigs) ... ok test_unequal_diff_exp (__main__.testSigFigs) ... expected failure test_unequal_same_exp (__main__.testSigFigs) ... expected failure ---------------------------------------------------------------------- Ran 5 tests in 0.081s OK (expected failures=3)

谢谢你们的反馈。


0
投票
无需额外的库,Python 支持

a < x < b

 作为范围表达式:

def test_abserr(): abserr=1e-4 expect=1.3456 result=calculate() assert expect-abserr <= result <= expect+abserr

def test_relerr(): relerr=1e-4 expect=1.3456 result=calculate() assert expect*(1-relerr) <= result <= expect*(1+relerr)
如果您使用 pytest,如果断言错误超出范围,您将获得结果和预期值,这对于找出问题所在有很大帮助。

如果您使用 Pandas,它有一个很好的

interval 类,可以更清晰地编写并避免重复。这可以与 pytest 结合使用来获取超出范围的值。

from pandas import Interval # plus or minus closed interval [-1,1] pm=Interval(-1,1,'both') def test_stuff(): # absolute error test assert result in expect+pm*abserr # relative error test assert result in expect*(1+pm*relerr)
    
© www.soinside.com 2019 - 2024. All rights reserved.