Проверка того, является ли float эквивалентом целочисленного значения в python
В Python 3 я проверяю, является ли данное значение треугольным, т.е. оно может быть представлено как n (n + 1)/2 для некоторого натурального n
Могу ли я просто написать:
import math
def is_triangular1(x):
num=(1/2) * (math.sqrt(8*x+1)-1 )
return int(num)==num
Или мне нужно сделать это так?
epsilon = 0.000000000001
def is_triangular2(x):
num=(1/2) * (math.sqrt(8*x+1)-1 )
return abs(int(num) - num)<epsilon
Я проверил, что обе функции возвращают одинаковые результаты для х до 1 000 000. Но я не уверен, что, вообще говоря, int (x) == x всегда будет правильно определять, является ли число целым, из-за случаев, когда пример 5 представлен как 4.99999999999997 и т.д.
Насколько я знаю, второй способ является правильным, если я делаю это на C, но я не уверен в Python 3.
Ответы
Ответ 1
Вы хотите сделать последнее. В программировании на Python 3 приведен пример, приведенный ниже, как наиболее точный способ сравнения
def equal_float(a, b):
#return abs(a - b) <= sys.float_info.epsilon
return abs(a - b) <= chosen_value #see edit below for more info
Кроме того, поскольку epsilon - это "наименьшая разница, машина может различать два числа с плавающей запятой", вы захотите использовать <= в своей функции.
Изменить. Прочитав комментарии ниже, я оглянулся на книгу, и в ней конкретно говорится: "Вот простая функция для сравнения поплавков для равенства с пределом точности машин". Я считаю, что это был всего лишь пример для сопоставления поплавков с предельной точностью, но тот факт, что ошибка введена во многих вычислениях с плавающей запятой, это редко бывает когда-либо использоваться. Я охарактеризовал его как "самый точный" способ сравнения в моем ответе, который в некотором смысле является истинным, но редко, что предполагается при сравнении поплавков или целых чисел с плавающими. Выбор значения (например: 0,00000000001) на основе "проблемного домена" функции вместо использования sys.float_info.epsilon - правильный подход.
Спасибо С.Лотту и Свен Марнах за их исправления, и я прошу прощения, если я привел кого-то по неправильному пути.
Ответ 2
Существует функция is_integer
в типе float python:
>>> float(1.0).is_integer()
True
>>> float(1.001).is_integer()
False
>>>
Ответ 3
У ваших реализаций есть проблемы. На самом деле может случиться так, что вы получите что-то вроде 4.999999999999997
, поэтому использование int()
не является вариантом.
Я бы выбрал совершенно другой подход: сначала предположим, что ваш номер треугольный и вычислить, что n
будет в этом случае. На этом первом шаге вы можете круто обходиться, так как нужно только получить результат правильно, если число фактически треугольное. Затем вычислите n * (n + 1) / 2
для этого n
и сравните результат с x
. Теперь вы сравниваете два целых числа, поэтому никаких неточностей не осталось.
Вычисление n
можно упростить, разложив
(1/2) * (math.sqrt(8*x+1)-1) = math.sqrt(2 * x + 0.25) - 0.5
и используя это
round(y - 0.5) = int(y)
для положительного y
.
def is_triangular(x):
n = int(math.sqrt(2 * x))
return x == n * (n + 1) / 2
Ответ 4
Python имеет класс Decimal
(в модуль Decimal
), который вы могли бы использовать, чтобы избежать неточности поплавков.
Ответ 5
floats может точно представлять все целые числа в своем диапазоне - равенство с плавающей запятой является только сложным, если вы заботитесь о бит после точки. Итак, до тех пор, пока все вычисления в вашей формуле возвращают целые числа для интересующих вас случаев, int (num) == num совершенно безопасно.
Итак, нам нужно доказать, что для любого треугольного числа каждая часть математики, которую вы делаете, может быть выполнена с помощью целочисленной арифметики (и все, что выходит как нецелое число, должно означать, что x не является треугольным):
Для начала мы можем предположить, что x должно быть целым числом - это требуется в определении "треугольного числа".
В этом случае 8 * x + 1 также будет целым числом, так как целые числа закрываются относительно + и *.
math.sqrt() возвращает float; но если х треугольно, то квадратный корень будет целым числом, т.е. снова точно представленным.
Итак, для всех x, которые должны возвращать true в ваших функциях, int (num) == num будет true, и поэтому ваш istriangular1 всегда будет работать. Единственным моментом, который упоминается в комментариях к вопросу, является то, что Python 2 по умолчанию выполняет целочисленное деление так же, как C - int/int = > int, усекает, если результат не может быть представлен точно как int, Итак, 1/2 == 0. Это исправлено в Python 3 или с линией
from __future__ import division
в верхней части вашего кода.
Ответ 6
Я думаю, что модуль десятичный - это то, что вам нужно
Ответ 7
Вы можете округлить свой номер, например. 14 знаков после запятой или меньше:
>>> round(4.999999999999997, 14)
5.0
PS: двойная точность составляет около 15 знаков после запятой
Ответ 8
Трудно спорить со стандартами.
В C99 и POSIX стандарт для округления float к int определяется nearint(). Важной концепцией является направление округления и соглашение об округлении, специфичное для локали.
Предполагая, что соглашение общее округление, это то же самое, что и соглашение C99 в Python:
#!/usr/bin/python
import math
infinity = math.ldexp(1.0, 1023) * 2
def nearbyint(x):
"""returns the nearest int as the C99 standard would"""
# handle NaN
if x!=x:
return x
if x >= infinity:
return infinity
if x <= -infinity:
return -infinity
if x==0.0:
return x
return math.floor(x + 0.5)
Если вы хотите больше контролировать округление, подумайте об использовании десятичного модуля и выберите соглашение округления, которое вы хотите использовать. Например, вы можете использовать "Округление банкиров" .
Как только вы определились с соглашением, округлите до int и сравните с другим int.
Ответ 9
Python по-прежнему использует одно и то же представление с плавающей запятой, а операции C - это, поэтому второй правильный путь.
Ответ 10
Под капотом тип с плавающей точкой Python является двойником C.
Наиболее надежным способом было бы получить ближайшее целое число до num, а затем проверить, удовлетворяют ли эти целые значения следующему свойству:
import math
def is_triangular1(x):
num = (1/2) * (math.sqrt(8*x+1)-1 )
inum = int(round(num))
return inum*(inum+1) == 2*x # This line uses only integer arithmetic