【发布时间】:2014-03-09 12:56:51
【问题描述】:
我目前正在实施 Fraction class 来训练我的 OOP 技能,但我遇到了一个问题...
- 分数和分数
- 整数和分数
- 分数和整数
我想同时添加:
- 分数和浮点数
- 浮点数和分数
因为当我使用整数时,我所做的就是使它们成为分母为 1 的 Fraction,我想在使用 float 时创建一个代表给定 float 的 Fraction。这就是我的问题所在。
首先,看懂我的Fractionclass所需的最少代码:
class Fraction(object):
def __init__(self,num,den=1,reduce=True):
# only accept integers or convertable strings
if not(type(num) == int and type(den) == int):
if type(num) == str:
try:
num = int(num)
except ValueError:
raise RuntimeError("You can only pass to the numerator and \
denominator integers or integer convertable strings!")
else:
raise RuntimeError("You can only pass to the numerator and \
denominator integers or integer convertable strings!")
if type(den) == str:
try:
den = int(den)
except ValueError:
raise RuntimeError("You can only pass to the numerator and \
denominator integers or integer convertable strings!")
else:
raise RuntimeError("You can only pass to the numerator and \
denominator integers or integer convertable strings!")
# don't accept fractions with denominator 0
if den == 0:
raise ZeroDivisionError("The denominator must not be 0")
# if both num and den are negative, flip both
if num < 0 and den < 0:
num = abs(num)
den = abs(num)
# if only the den is negative, change the "-" to the numerator
elif den < 0:
num *= -1
den = abs(den)
self.num = num
self.den = den
# the self.auto is a variable that will tell us if we are supposed to
#automatically reduce the Fraction to its lower terms. when doing some
#maths, if either one of the fractions has self.auto==False, the result
#will also have self.auto==False
self.auto = reduce
if self.auto:
self.reduce()
def float_to_fraction(f):
'''should not be called by an instance of a Fraction, since it does not\
accept, purposedly, the "self" argument. Instead, call it as\
Fraction.float_to_fraction to create a new Fraction with a given float'''
# Start by making the number a string
f = str(f)
exp = ""
# If the number has an exponent (is multiplied by 10 to the power of sth
#store it for later.
if "e" in f:
# Get the "e+N" or "e-N"
exp = f[f.index("e"):]
# Slice the exponent from the string
f = f[:f.index("e")]
# Start the numerator and the denominator
num = "0"
den = "1"
# Variable to check if we passed a ".", marking the decimal part of a
#number
decimal = False
for char in f:
if char != ".":
# Add the current char to the numerator
num += char
if decimal:
# If we are to the right of the ".", also add a 0 to the
#denominator to keep proportion
den += "0"
# Slice parsed character
f = f[1:]
if char == ".":
# Tell the function we are now going to the decimal part of the
#float.
decimal = True
# Slice the "."
f = f[1:]
# Parse the exponent, if there is one
if exp != "":
# If it is a negative exponent, we should make the denominator bigger
if exp[1] == "-":
# Add as many 0s to the den as the absolute value of what is to
#the right of the "-" sign. e.g.: if exp = "e-12", add 12 zeros
den += "0"*int(exp[2:])
# Same stuff as above, but in the numerator
if exp[1] == "+":
num += "0"*int(exp[2:])
# Last, return the Fraction object with the parsed num and den!
return Fraction(int(num),int(den))
我的float_to_fraction() 函数100% 准确地将给定的float 转换为Fraction。但我记得在我的数学课上,循环小数的 n 位数长循环,如 0.123123123123... 或 0.(123) 可以写成带有 numerator = cycle 和 denominator = (as many 9s as the length of the cycle) 的分数形式:
123/999 = 0.(123)
3/9 (=1/3) = 0.(3); 142857/999999 (=1/7) = 0.(142857)等等等等……
但是通过这个实现,如果我将像 1/3 这样的参数传递给float_to_fraction(),它将解析有限的“0.3333333333333333”,返回这个分数:3333333333333333/10000000000000000。这是准确的,因为我向函数传递了一个有限的数字!如何在这个函数中实现一种识别循环小数的方法,这样我就可以返回一个带有 9s 负载的分母,而不是带有 denominator = 10^n 的 Fraction。
【问题讨论】:
-
你可能想看看这个:find rational approximation to given real number。虽然代码是 C,但它应该可以帮助您入门。
-
看看
fractions.Fraction.limit_denominator和它的source code。这不是一项简单的工作。 -
您的
float_to_fraction()函数没有 100% 准确地转换。浮点数具有二进制指数,但您依赖于以 10 为底的表示。将浮点数转换为精确分数的正确方法是检查其二进制表示;您会看到您需要将尾数除以(或乘以)2**exponent。 -
这行不通。首先不要将数字放在二进制浮点数中。