Природа чисел с плавающей точкой означает, что нет смысла проверять , является ли число с плавающей точкой рациональным, поскольку все числа с плавающей точкой на самом деле являются дробями вида n / 2 e .Однако вы, возможно, захотите узнать, существует ли простая дробь (с небольшим знаменателем, а не с большой степенью 2), которая близко приближается к данному числу с плавающей запятой.
Дональд Кнут обсуждает эту последнюю проблему в Искусство компьютерного программирования том II.Смотрите ответ к упражнению 4.53-39.Идея состоит в том, чтобы искать дробь с наименьшим знаменателем в пределах диапазона, расширяя конечные точки диапазона как непрерывные дроби до тех пор, пока их коэффициенты равны, а затем, когда они различаются, принять простейшее значение между ними.Вот довольно простая реализация в Python:
from fractions import Fraction
from math import modf
def simplest_fraction_in_interval(x, y):
"""Return the fraction with the lowest denominator in [x,y]."""
if x == y:
# The algorithm will not terminate if x and y are equal.
raise ValueError("Equal arguments.")
elif x < 0 and y < 0:
# Handle negative arguments by solving positive case and negating.
return -simplest_fraction_in_interval(-y, -x)
elif x <= 0 or y <= 0:
# One argument is 0, or arguments are on opposite sides of 0, so
# the simplest fraction in interval is 0 exactly.
return Fraction(0)
else:
# Remainder and Coefficient of continued fractions for x and y.
xr, xc = modf(1/x);
yr, yc = modf(1/y);
if xc < yc:
return Fraction(1, int(xc) + 1)
elif yc < xc:
return Fraction(1, int(yc) + 1)
else:
return 1 / (int(xc) + simplest_fraction_in_interval(xr, yr))
def approximate_fraction(x, e):
"""Return the fraction with the lowest denominator that differs
from x by no more than e."""
return simplest_fraction_in_interval(x - e, x + e)
И вот некоторые результаты:
>>> approximate_fraction(6.75, 0.01)
Fraction(27, 4)
>>> approximate_fraction(math.pi, 0.00001)
Fraction(355, 113)
>>> approximate_fraction((1 + math.sqrt(5)) / 2, 0.00001)
Fraction(377, 233)