Swift中的十进制到分数转换

owl*_*ipe 7 calculator fractions ios swift swift2

我正在构建一个计算器,并希望它自动将每个小数转换为分数.因此,如果用户计算答案为"0.333333 ..."的表达式,则返回"1/3".对于"0.25",它将返回"1/4".使用GCD,如此处所示(十进制到分数转换),我已经想出如何将任何有理的终止十进制转换为十进制,但这对任何重复的小数都不起作用(如.333333).

堆栈溢出的每个其他函数都在Objective-C中.但我需要一个快速应用程序中的功能!所以这个(/sf/answers/940116621/)的翻译版本会很棒!

关于如何将有理数或重复/无理小数转换为分数(即将"0.1764705882 ..."转换为3/17)的任何想法或解决方案都会很棒!

Mar*_*n R 38

如果要将计算结果显示为有理数,那么唯一100%正确的解决方案是在所有计算中使用有理算法,即所有中间值都存储为一对整数(numerator, denominator),所有加法,乘法,除法等都是使用有理数的规则完成.

一旦将结果分配给二进制浮点数, 例如Double,信息就会丢失.例如,

let x : Double = 7/10
Run Code Online (Sandbox Code Playgroud)

在商店x近似0.7,因为该数量不能精确作为表示Double.从

print(String(format:"%a", x)) // 0x1.6666666666666p-1
Run Code Online (Sandbox Code Playgroud)

人们可以看到它x具有价值

0x16666666666666 * 2^(-53) = 6305039478318694 / 9007199254740992
                           ? 0.69999999999999995559107901499373838305
Run Code Online (Sandbox Code Playgroud)

因此,x作为一个有理数的正确表示将是 6305039478318694 / 9007199254740992,但这当然不是你所期望的.你期望的是7/10,但还有另一个问题:

let x : Double = 69999999999999996/100000000000000000
Run Code Online (Sandbox Code Playgroud)

分配完全相同的值x,它与0.7a的精度无法区分 Double.

所以应该x显示为7/1069999999999999996/100000000000000000

如上所述,使用有理算法将是完美的解决方案.如果这不可行,那么您可以将Double返回转换为具有给定精度的有理数.(以下内容摘自Swift的双精度LCM算法.)

连续分数 是创建(有限或无限)分数h n/k n的有效方法,它是对给定实数x的任意良好近似,这里是Swift中可能的实现:

typealias Rational = (num : Int, den : Int)

func rationalApproximationOf(x0 : Double, withPrecision eps : Double = 1.0E-6) -> Rational {
    var x = x0
    var a = floor(x)
    var (h1, k1, h, k) = (1, 0, Int(a), 1)

    while x - a > eps * Double(k) * Double(k) {
        x = 1.0/(x - a)
        a = floor(x)
        (h1, k1, h, k) = (h, k, h1 + Int(a) * h, k1 + Int(a) * k)
    }
    return (h, k)
}
Run Code Online (Sandbox Code Playgroud)

例子:

rationalApproximationOf(0.333333) // (1, 3)
rationalApproximationOf(0.25)     // (1, 4)
rationalApproximationOf(0.1764705882) // (3, 17)
Run Code Online (Sandbox Code Playgroud)

默认精度为1.0E-6,但您可以根据需要进行调整:

rationalApproximationOf(0.142857) // (1, 7)
rationalApproximationOf(0.142857, withPrecision: 1.0E-10) // (142857, 1000000)

rationalApproximationOf(M_PI) // (355, 113)
rationalApproximationOf(M_PI, withPrecision: 1.0E-7) // (103993, 33102)
rationalApproximationOf(M_PI, withPrecision: 1.0E-10) // (312689, 99532)
Run Code Online (Sandbox Code Playgroud)

Swift 3版本:

typealias Rational = (num : Int, den : Int)

func rationalApproximation(of x0 : Double, withPrecision eps : Double = 1.0E-6) -> Rational {
    var x = x0
    var a = x.rounded(.down)
    var (h1, k1, h, k) = (1, 0, Int(a), 1)

    while x - a > eps * Double(k) * Double(k) {
        x = 1.0/(x - a)
        a = x.rounded(.down)
        (h1, k1, h, k) = (h, k, h1 + Int(a) * h, k1 + Int(a) * k)
    }
    return (h, k)
}
Run Code Online (Sandbox Code Playgroud)

例子:

rationalApproximation(of: 0.333333) // (1, 3)
rationalApproximation(of: 0.142857, withPrecision: 1.0E-10) // (142857, 1000000)
Run Code Online (Sandbox Code Playgroud)

或者 - 正如@brandonscript所建议的那样 - 使用a struct Rational和初始值设定项:

struct Rational {
    let numerator : Int
    let denominator: Int

    init(numerator: Int, denominator: Int) {
        self.numerator = numerator
        self.denominator = denominator
    }

    init(approximating x0: Double, withPrecision eps: Double = 1.0E-6) {
        var x = x0
        var a = x.rounded(.down)
        var (h1, k1, h, k) = (1, 0, Int(a), 1)

        while x - a > eps * Double(k) * Double(k) {
            x = 1.0/(x - a)
            a = x.rounded(.down)
            (h1, k1, h, k) = (h, k, h1 + Int(a) * h, k1 + Int(a) * k)
        }
        self.init(numerator: h, denominator: k)
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

print(Rational(approximating: 0.333333))
// Rational(numerator: 1, denominator: 3)

print(Rational(approximating: .pi, withPrecision: 1.0E-7))
// Rational(numerator: 103993, denominator: 33102)
Run Code Online (Sandbox Code Playgroud)