Python Decimal - проверка, если целое число

Я использую библиотеку Decimal в Python и печатаю значения, используя format(value, 'f'), где значение равно Decimal. Я получаю числа в форме 10.00000, которая отражает точность десятичного числа. Я знаю, что float поддерживает is_integer, но, похоже, отсутствует аналогичный API для десятичных знаков. Мне было интересно, есть ли способ обойти это.

Ответ 1

Вы можете использовать операцию modulo для проверки наличия нецелого остатка:

>>> from decimal import Decimal
>>> Decimal('3.14') % 1 == 0
False
>>> Decimal('3') % 1 == 0
True
>>> Decimal('3.0') % 1 == 0
True

Ответ 2

Попробуйте math.floor(val) == val или val == int(val).

Ответ 3

Математическое решение состоит в том, чтобы преобразовать ваше десятичное число в целое, а затем проверить его равенство с вашим числом.

Поскольку Decimal может иметь произвольную точность, вы не должны преобразовывать ее в int или float.

К счастью, у класса Decimal есть значение to_integral_value которое выполняет преобразование для вас. Вы можете принять решение как это:

def is_integer(d):
    return d == d.to_integral_value()

Пример:

from decimal import Decimal

d_int = Decimal(3)
assert is_integer(d_int)

d_float = Decimal(3.1415)
assert not is_integer(d_float)

Смотрите: http://docs.python.org/2/library/decimal.html#decimal.Decimal.to_integral_value

Ответ 4

Десятичный имеет метод "скрытый", называемый _isinteger(), который работает подобно методу float is_integer():

>>> Decimal(1)._isinteger()
True
>>> Decimal(1.1)._isinteger()
Traceback (most recent call last):
  File "C:\Program Files (x86)\Wing IDE 4.1\src\debug\tserver\_sandbox.py", line 1, in <module>
    # Used internally for debug sandbox under external interpreter
  File "C:\Python26\Lib\decimal.py", line 649, in __new__
    "First convert the float to a string")
TypeError: Cannot convert float to Decimal.  First convert the float to a string

Как вы можете видеть, вам придется поймать исключение. В качестве альтернативы вы можете выполнить тест по значению ПЕРЕД тем, как вы передадите его в Decimal, используя метод float, как вы упомянули, или используя isinstance.

Ответ 5

Другой способ:

>>> float(Decimal('3.14')).is_integer()
False
>>> float(Decimal('3.0')).is_integer()
True

Также возможно использование так называемого "магического метода" (или двойного подчеркивания) __float__():

>>> Decimal('3.14').__float__().is_integer()
False
>>> Decimal('3.0').__float__().is_integer()
True

Тесты производительности

Истекшее время за 10 ^ 6 итераций:

0.462s | d == d.to_integral_value()
0.527s | d % 1 == 0
0.998s | d == int(d)
1.023s | float(d).is_integer()

Тестовый код:

from time import perf_counter as perf
from decimal import Decimal

l = list(map(Decimal, range(1000000)))

t = perf()
all(d == d.to_integral_value() for d in l)
print(f'{perf()-t:.3f}s', '| d == d.to_integral_value()')

t = perf()
all(d % 1 == 0 for d in l)
print(f'{perf()-t:.3f}s', '| d % 1 == 0')

t = perf()
all(d == int(d) for d in l)
print(f'{perf()-t:.3f}s', '| d == int(d)')

t = perf()
all(float(d).is_integer() for d in l)
print(f'{perf()-t:.3f}s', '| float(d).is_integer()')