Лучший метод для ограничения точности вычислений в Python doctest - PullRequest
0 голосов
/ 24 октября 2018

Я работаю над Python-версией библиотеки, которая имеет другие версии с хорошо установленными результатами.Из-за этого у меня есть строки в моих строках документов, такие как

>>> air_f(0,0,0,0.9,300.,1.)
-95019.5943231

, где точность находится в пределах того, о чем должны соглашаться все версии.Если я запускаю doctest для этого кода, вместо этого он ожидает значение -95019.59432308903, которое соответствует, но с более высокой точностью.В другом случае 8.50371537341e-04 отклоняется в пользу 0.0008503715373413415, который имеет только одну цифру точности, но другого формата.

Я знаю несколько способов справиться с этим.Я мог бы пройтись и изменить все числа с полной точностью или добавить операторы форматирования печати перед каждым из них, чтобы они соответствовали только точности.Проблема состоит в том, что каждый из 50+ отдельных файлов в библиотеке может содержать десятки этих строк, и конкретная строка формата отличается для каждого.Я видел numtest , но я не знаю, зрел ли он (плюс у меня проблемы с его установкой).

Я надеюсь, что есть какой-то способ расширить конструкцию

if __name__ == '__main__':
    import doctest
    doctest.testmod()

, поэтому doctest использует более слабый критерий равенства с плавающей точкой.Добавление этого в 50 файлов намного более управляемо, чем изменение более 1000 отдельных строк.Я не знаком с doctest, поэтому не знаю, возможно ли это или как это сделать.Мысли?

1 Ответ

0 голосов
/ 25 октября 2018

Я потратил время на просмотр doctest и придумал следующее.Кажется, это работает для всех способов форматирования моих float, но я определенно буду признателен за предложения о том, как сделать его более надежным, как передать все параметры и т. Д. (В моем случае это особенно легко, так как все моих тестов документации будут плавать или списки плаваний, поэтому я не проверял другие варианты использования.)


import doctest

def extractsigfmt(numstr):
    """Find a standard formatter from a string.
    From a string representing a float, find the number of
    significant digits and return a string formatter to standardize
    its representation.
    """
    # Pull out the digits term, removing newline and accounting
    # for either E or e in exponential notation
    digs = numstr.lower().rstrip('\n').split('e')[0]
    # Separate from decimal point, removing sign and leading zeros
    res = digs.split('.')
    if len(res) == 1:
        l, r = res, ''
    else:
        l, r = res
    l = l.lstrip(' -+0')
    nsig = len(l) + len(r)
    # Create a standardized exponential formatter
    fmt = '{:+' + '{0:d}.{1:d}'.format(nsig+6,nsig-1) + 'e}'
    return fmt

# Create new OutputChecker class
class fltOutputChecker(doctest.OutputChecker):
    """OutputChecker customized for floats.
    Overrides check_output to compare standardized floats.
    """
    def check_output(self,want,got,optionflags):
        """Check whether actual output agrees with expected.
        Return True iff the actual output agrees with the expected
        output within the number of significant figures of the
        expected output.
        """
        fmt = extractsigfmt(want)
        want_std = fmt.format(float(want))
        got_std = fmt.format(float(got))
        return (want_std == got_std)

def testfun(n):
    """Function to test new OutputChecker on.
    >>> testfun(0)
    -6.96239965190e+05
    >>> testfun(1)
    8.01977741e-10
    >>> testfun(2)
    -95019.5943231
    >>> testfun(3)
    0.164195952060
    >>> testfun(4)
    -0.687329742959e-3
    >>> testfun(5)
    0.876543210e3
    """
    testlist = [-696239.9651898777,8.01977740740741e-10,
        -95019.59432308903,0.1641959520603997,
        -0.0006873297429586108,876.54320956893102]
    return testlist[n]

# Construct and run doctest on this function
finder = doctest.DocTestFinder()
dtlist = finder.find(testfun)
checker = fltOutputChecker()
runner = doctest.DocTestRunner(checker)
for dt in dtlist:
    runner.run(dt)
results = runner.summarize(verbose=True)
...