【问题标题】:Float to Fraction conversion : recognizing cyclic decimals浮点到分数的转换:识别循环小数
【发布时间】:2014-03-09 12:56:51
【问题描述】:

我目前正在实施 Fraction class 来训练我的 OOP 技能,但我遇到了一个问题...

  • 分数和分数
  • 整数和分数
  • 分数和整数

我想同时添加:

  • 分数和浮点数
  • 浮点数和分数

因为当我使用整数时,我所做的就是使它们成为分母为 1 的 Fraction,我想在使用 float 时创建一个代表给定 floatFraction。这就是我的问题所在。

首先,看懂我的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 = cycledenominator = (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^nFraction

【问题讨论】:

  • 你可能想看看这个:find rational approximation to given real number。虽然代码是 C,但它应该可以帮助您入门。
  • 看看fractions.Fraction.limit_denominator 和它的source code。这不是一项简单的工作。
  • 您的float_to_fraction() 函数没有 100% 准确地转换。浮点数具有二进制指数,但您依赖于以 10 为底的表示。将浮点数转换为精确分数的正确方法是检查其二进制表示;您会看到您需要将尾数除以(或乘以)2**exponent
  • 这行不通。首先不要将数字放在二进制浮点数中。

标签: python math fractions


【解决方案1】:

将十进制表达式转换为近似有理数的最佳方法是通过连分数展开。

对于 x=0.123123123123,这将导致

r=x, 
a[0]=floor(r)=0, r=r-a[0], r=1/r=8.121951219520,
a[1]=floor(r)=8, r=r-a[1], r=1/r=8.199999999453,
a[2]=floor(r)=8, r=r-a[2], r=1/r=5.000000013653,
a[3]=floor(r)=5, r=r-a[3], r=1/r=73243975.48780,

此时r-a[3]&lt;1e-5 迭代停止。找到的有理近似是

x=1/(8+1/(8+1/5))=1/(8+5/41)=41/333 (=123/999)

中间收敛点是

1/8=0.125,      x- 1/8   = -0.001876876877,
1/(8+1/8)=8/65, x- 8/65  =  0.0000462000460,
41/333,         x-41/333 = -1.231231231231e-13.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多