Python: a может быть округлен до b в общем случае - PullRequest
7 голосов
/ 03 октября 2010

Как часть некоторого кода модульного тестирования, который я пишу, я написал следующую функцию. Цель этого - определить, можно ли округлить «a» до «b», независимо от того, насколько точны «a» или «b».

def couldRoundTo(a,b):
    """Can you round a to some number of digits, such that it equals b?"""
    roundEnd = len(str(b))
    if a == b:
        return True
    for x in range(0,roundEnd):
        if round(a,x) == b:
            return True
    return False

Вот некоторые выходные данные функции:

>>> couldRoundTo(3.934567892987, 3.9)
True
>>> couldRoundTo(3.934567892987, 3.3)
False
>>> couldRoundTo(3.934567892987, 3.93)
True
>>> couldRoundTo(3.934567892987, 3.94)
False

Насколько я могу сказать, это работает. Тем не менее, я боюсь полагаться на это, учитывая, что у меня нет полного понимания вопросов, касающихся точности с плавающей точкой. Может кто-нибудь сказать мне, если это подходящий способ для реализации этой функции? Если нет, как я могу улучшить это?

Ответы [ 4 ]

3 голосов
/ 03 октября 2010

Может кто-нибудь сказать мне, если это подходящий способ для реализации этой функции?

Это зависит.Данная функция будет вести себя удивительно, если b не точно равно значению, которое обычно получается непосредственно из преобразования в десятичное в двоичное с плавающей точкой.

Например:

>>> print(0.1, 0.2/2, 0.3/3)
0.1 0.1 0.1
>>> couldRoundTo(0.123, 0.1)
True
>>> couldRoundTo(0.123, 0.2/2)
True
>>> couldRoundTo(0.123, 0.3/3)
False

Сбой, потому что вычисление 0.3 / 3 приводит к несколько другому представлению, чем 0.1 и 0.2 / 2round(0.123, 1)).

Если нет,как я могу улучшить его?

Правило большого пальца: если ваши вычисления каким-либо образом связаны с десятичными цифрами, просто используйте Decimal, чтобы избежать всех циклических сбоев с потерями по основанию 2.

В частности, Decimal включает помощника по имени quantize, который упрощает эту проблему:

from decimal import Decimal

def roundable(a, b):
    a = Decimal(str(a))
    b = Decimal(str(b))
    return a.quantize(b) == b
1 голос
/ 03 октября 2010

Один из способов сделать это:

def could_round_to(a, b):
    (x, y) = map(len, str(b).split('.'))
    round_format = "%" + "%d.%df"%(x, y)
    return round_format%a == str(b) 

Сначала мы берем количество цифр до и после десятичного числа в x и y. Затем мы создаем формат, такой как %x.yf. Затем мы добавляем a в строку формата.

>>> "%2.2f"%123.1234
'123.12'
>>> "%2.2f"%123.1264
'123.13'
>>> "%3.2f"%000.001
'0.00'

Теперь осталось только сравнить строки.

0 голосов
/ 03 октября 2010

Если вы хотите проверить, будет ли функция round округляться до цели, то вы правы.В противном случае (что еще является целью?), Если у вас есть сомнения, вы должны использовать decimal module

0 голосов
/ 03 октября 2010

Единственное, чего я боюсь, это преобразования строк в числа с плавающей запятой при интерпретации литералов с плавающей запятой (как в http://docs.python.org/reference/lexical_analysis.html#floating-point-literals).. Я не знаю, есть ли какая-либо гарантия, что с плавающей запятойлитерал будет вычислять до числа с плавающей точкой, которое ближе всего к данной строке . Этот упомянутый раздел является местом в спецификации, где я ожидал бы такую ​​гарантию.

Например, Javaгораздо более конкретно о том, что ожидать от строкового литерала. Из документации Double.valueOf (String) :

[...] [аргумент] рассматриваетсякак точное десятичное значение в обычной «компьютеризированной научной нотации» или как точное шестнадцатеричное значение, затем это точное числовое значение концептуально преобразуется в «бесконечно точное» двоичное значение, которое затем округляется до типа double обычным округлением доправило IEEE 754 с плавающей запятой [...]

Если вы не можете найти такоеЕсли вы гарантированно где-нибудь в документации по Python, вам просто повезет, потому что некоторые более ранние библиотеки с плавающей точкой (на которые может положиться Python) преобразуют строку только в число с плавающей точкой поблизости, а не в лучшее из доступных.

К сожалению, мне кажется, что ни round, ни float, ни спецификация для с плавающей запятой буквально не дают вам никакой полезной гарантии.

...