【问题标题】:Assert equality of floating point values to a significant figure tolerance将浮点值的相等性断言为有效数字容差
【发布时间】:2013-10-28 20:05:48
【问题描述】:

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

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) 是否有人已经编写了以这种方式工作的自定义断言语句?

【问题讨论】:

  • 谨慎定义“重要数字”。这不仅仅是小数位数......通常定义中不计算小数点后紧接的零,因此您的示例 Ixx_ther 和 Iyy_ther 都将被视为具有三个有效数字。

标签: python unit-testing assert scientific-computing


【解决方案1】:

Re:是否有内置解决方案:如果您可以将 numpy 作为依赖项,请查看 numpy.testing.

这是一个例子(逐字逐句来自assert_allclose docs):

>>> 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 的答案几乎相同(加上对数组参数和无穷大的处理)。

【讨论】:

  • 谢谢,这正是我想要的。我正在使用 numpy,但从未见过它有一个测试模块。
  • @steve855:在np.allclose的实际实现链接中编辑
【解决方案2】:

这是对answer I left on another question 的改造。

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

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

【讨论】:

  • 谢谢,这是一个非常优雅的解决方案,虽然我想我会使用@Zhenya 提出的numpy测试模块
  • (+1) 这几乎总是有效&gt;&gt;&gt; AlmostEqual(1e320, 1e320, 2) False
【解决方案3】:

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

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 的 string formatting mini-language 将浮点数格式化为给定的方式。所以我们可以做的是强制它们以指数表示法格式化,例如输入0.1230.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 的方式,可能会出现一个问题。不过能理解这个想法。

【讨论】:

    【解决方案4】:

    感谢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)
    

    感谢两位的反馈。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-12
      • 1970-01-01
      • 1970-01-01
      • 2014-10-04
      • 2018-04-03
      相关资源
      最近更新 更多