小数到小数在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/10
或69999999999999996/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)