如何对浮点输出执行单元测试? - python

How to perform unittest for floating point outputs? - python

假设我正在为一个 returns 浮点数的函数编写单元测试,我可以按照我的机器完全精确地这样做:

>>> import unittest
>>> def div(x,y): return x/float(y)
... 
>>>
>>> class Testdiv(unittest.TestCase):
...     def testdiv(self):
...             assert div(1,9) == 0.1111111111111111
... 
>>> unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

相同的完整浮点精度在 OS/distro/machine 中是否相同?

我可以尝试四舍五入并进行这样的单元测试:

>>> class Testdiv(unittest.TestCase):
...     def testdiv(self):
...             assert round(div(1,9),4) == 0.1111
... 
>>>

我也可以使用 log(output) 进行断言,但为了保持固定的小数精度,我仍然需要进行舍入或截断。

但是用 Python 方式处理浮点输出的单元测试还有什么其他方法吗?

unittest.TestCaseclass有比较浮点数的具体方法:assertAlmostEqual and assertNotAlmostEqual。引用文档:

assertAlmostEqual(first, second, places=7, msg=None, delta=None) assertNotAlmostEqual(first, second, places=7, msg=None, delta=None)

Test that first and second are approximately (or not approximately) equal by computing the difference, rounding to the given number of decimal places (default 7), and comparing to zero. Note that these methods round the values to the given number of decimal places (i.e. like the round() function) and not significant digits.

If delta is supplied instead of places then the difference between first and second must be less or equal to (or greater than) delta.

因此,您可以像这样测试函数:

self.assertAlmostEqual(div(1, 9), 0.1111111111111111)  # round(a-b, 7) == 0
self.assertAlmostEqual(div(1, 9), 0.1111, 4)           # round(a-b, 4) == 0

附带说明,除非您使用 pytest 作为测试运行程序,否则您应该更喜欢 TestCase.assert* 方法而不是裸露的 assert 语句,因为测试失败消息由方法通常提供更多信息。

Python 中 float 的精度取决于底层 C 表示。来自 Tutorial/Floating Point Arithmetic: Issues and Limitations, 15.1:

Almost all machines today (November 2000) use IEEE-754 floating point arithmetic, and almost all platforms map Python floats to IEEE-754 “double precision”.


至于测试,更好的想法是使用现有功能,例如TestCase.assertAlmostEqual:

assertAlmostEqual(first, second, places=7, msg=None, delta=None)

Test that first and second are approximately (or not approximately) equal by computing the difference, rounding to the given number of decimal places (default 7), and comparing to zero. If delta is supplied instead of places then the difference between first and second must be less or equal to (or greater than) delta.

示例:

import unittest

def div(x, y): return x / float(y)

class Testdiv(unittest.TestCase):
    def testdiv(self):
        self.assertAlmostEqual(div(1, 9), 0.1111111111111111)
        self.assertAlmostEqual(div(1, 9), 0.1111, places=4)

unittest.main() # OK

如果你喜欢坚持 assert 声明,你可以使用 math.isclose (Python 3.5+):

import unittest, math

def div(x, y): return x / float(y)

class Testdiv(unittest.TestCase):
    def testdiv(self):
        assert math.isclose(div(1, 9), 0.1111111111111111)

unittest.main() # OK

math.close 的默认相对公差是 1e-09,“保证两个值在大约 9 个小数位内相同。”。有关 math.close 的更多信息,请参阅 PEP 485