小数到小数在Swift中的转换

我正在build立一个计算器,并希望它自动将每个小数转换成一个分数。 所以如果用户计算一个expression式的答案是“0.333333 …”,它会返回“1/3”。 对于“0.25”,它将返回“1/4”。 使用GCD,如这里发现的( 小数到小数转换 ),我已经想出了如何将任何有理数的终止小数转换成小数,但是这不适用于任何重复的小数(如.333333)。

这个堆栈溢出的其他函数在Objective-C中。 但我需要在我的快速应用程序中的function! 所以这个( https://stackoverflow.com/a/13430237/5700898 )的翻译版本会很好!

关于如何将一个合理的或重复的/非理性的小数转换成分数 (即将“0.1764705882 …”转换为3/17),任何想法或解决scheme将是伟大的!

如果要将计算结果显示为有理数,那么唯一100%正确的解决scheme是在所有计算中使用有理算术 ,即所有中间值都作为一对整数(numerator, denominator) ,所有加法,乘法,部门等是用有理数的规则来完成的。

只要将结果分配给二进制浮点数 (如Double ,信息就会丢失。 例如,

 let x : Double = 7/10 

x 近似0.7 ,因为这个数字不能完全表示为Double 。 从

 print(String(format:"%a", x)) // 0x1.6666666666666p-1 

人们可以看到x拥有价值

 0x16666666666666 * 2^(-53) = 6305039478318694 / 9007199254740992 ≈ 0.69999999999999995559107901499373838305 

所以x作为一个有理数的正确表示应该是6305039478318694 / 9007199254740992 ,但这当然不是你所期望的。 你期望的是7/10 ,但还有一个问题:

 let x : Double = 69999999999999996/100000000000000000 

x赋予完全相同的值,在Double的精度内,它与0.7是无法区分的。

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

如上所述,使用合理的算术将是一个完美的解决scheme。 如果这是不可行的,那么你可以将Double转换回给定精度的有理数。 (以下是Swift中双打LCMalgorithm 。)

续分数是一个有效的方法来创build一个(有限的或无限的)分数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) } 

例子:

 rationalApproximationOf(0.333333) // (1, 3) rationalApproximationOf(0.25) // (1, 4) rationalApproximationOf(0.1764705882) // (3, 17) 

默认精度是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) 

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) } 

例子:

 rationalApproximation(of: 0.333333) // (1, 3) rationalApproximation(of: 0.142857, withPrecision: 1.0E-10) // (142857, 1000000)