Подтвердить что ты не робот

Проверка того, является ли 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.

4b9b3361

Ответ 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