我正在构建一个计算器,并希望它自动将每个小数转换为小数。因此,如果用户计算出答案为“ 0.333333 ...”的表达式,它将返回“ 1/3”。对于“ 0.25”,它将返回“ 1/4”。使用此处(Decimal to fraction conversion)找到的GCD,我已经弄清楚了如何将任何有理的,终止的十进制转换为十进制,但是这不适用于任何重复的十进制(例如.333333)。
关于堆栈溢出的其他所有功能都在Objective-C中。但是我需要在我的快速应用程序中添加一个功能!因此,此(https://stackoverflow.com/a/13430237/5700898)的翻译版本会很好!
关于如何将有理数或重复数/无理数的小数转换为小数(即将“ 0.1764705882 ...”转换为3/17的任何想法或解决方案都不错!
如果要将计算结果显示为有理数那么唯一的100%正确的解决方案是在所有计算中都使用有理算术,即所有中间值都存储为一对整数(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
分配完全相同的值,与在0.7
精度范围内的Double
。
所以x
应该显示为7/10
还是显示为69999999999999996/100000000000000000
?
如上所述,使用有理算术将是完美的解决方案。如果这样做不可行,则可以将Double
转换为有理数具有给定的精度。(以下摘自Algorithm for LCM of doubles in Swift。)
Continued Fractions是创建分数([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)
或–由@brandonscript建议–具有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) } }
示例用法:
print(Rational(approximating: 0.333333)) // Rational(numerator: 1, denominator: 3) print(Rational(approximating: .pi, withPrecision: 1.0E-7)) // Rational(numerator: 103993, denominator: 33102)
之所以创建该类,也是因为我需要进行非常精确的计算,而对于迅速提供的类型,这是不可能的。所以我创建了自己的类型。
这是代码,我将在下面解释。
class Rational {
var alpha = 0
var beta = 0
init(_ a: Int, _ b: Int) {
if (a > 0 && b > 0) || (a < 0 && b < 0) {
simplifier(a,b,"+")
}
else {
simplifier(a,b,"-")
}
}
init(_ double: Double, accuracy: Int = -1) {
exponent(double, accuracy)
}
func exponent(_ double: Double, _ accuracy: Int) {
//Converts a double to a rational number, in which the denominator is of power of 10.
var exp = 1
var double = double
if accuracy != -1 {
double = Double(NSString(format: "%.\(accuracy)f" as NSString, double) as String)!
}
while (double*Double(exp)).remainder(dividingBy: 1) != 0 {
exp *= 10
}
if double > 0 {
simplifier(Int(double*Double(exp)), exp, "+")
}
else {
simplifier(Int(double*Double(exp)), exp, "-")
}
}
func gcd(_ alpha: Int, _ beta: Int) -> Int {
// Calculates 'Greatest Common Divisor'
var inti: [Int] = []
var multi = 1
var a = Swift.min(alpha,beta)
var b = Swift.max(alpha,beta)
for idx in 2...a {
if idx != 1 {
while (a%idx == 0 && b%idx == 0) {
a = a/idx
b = b/idx
inti.append(idx)
}
}
}
inti.map{ multi *= $0 }
return multi
}
func simplifier(_ alpha: Int, _ beta: Int, _ posOrNeg: String) {
//Simplifies nominator and denominator (alpha and beta) so they are 'prime' to one another.
let alpha = alpha > 0 ? alpha : -alpha
let beta = beta > 0 ? beta : -beta
let greatestCommonDivisor = gcd(alpha,beta)
self.alpha = posOrNeg == "+" ? alpha/greatestCommonDivisor : -alpha/greatestCommonDivisor
self.beta = beta/greatestCommonDivisor
}
}
typealias Rnl = Rational
func *(a: Rational, b: Rational) -> Rational {
let aa = a.alpha*b.alpha
let bb = a.beta*b.beta
return Rational(aa, bb)
}
func /(a: Rational, b: Rational) -> Rational {
let aa = a.alpha*b.beta
let bb = a.beta*b.alpha
return Rational(aa, bb)
}
func +(a: Rational, b: Rational) -> Rational {
let aa = a.alpha*b.beta + a.beta*b.alpha
let bb = a.beta*b.beta
return Rational(aa, bb)
}
func -(a: Rational, b: Rational) -> Rational {
let aa = a.alpha*b.beta - a.beta*b.alpha
let bb = a.beta*b.beta
return Rational(aa, bb)
}
extension Rational {
func value() -> Double {
return Double(self.alpha) / Double(self.beta)
}
}
extension Rational {
func rnlValue() -> String {
if self.beta == 1 {
return "\(self.alpha)"
}
else if self.alpha == 0 {
return "0"
}
else {
return "\(self.alpha) / \(self.beta)"
}
}
}
// examples:
let first = Rnl(120,45)
let second = Rnl(36,88)
let third = Rnl(2.33435, accuracy: 2)
let forth = Rnl(2.33435)
print(first.alpha, first.beta, first.value(), first.rnlValue()) // prints 8 3 2.6666666666666665 8 / 3
print((first*second).rnlValue()) // prints 12 / 11
print((first+second).rnlValue()) // prints 203 / 66
print(third.value(), forth.value()) // prints 2.33 2.33435
首先,我们有类本身。该类可以通过两种方式初始化:在Rational类中,alpha〜=分子和beta〜=分母
第一种方法是使用两个整数初始化类,其中with的第一个是分母,第二个是分母。该类将获得这两个整数,然后将它们减少到尽可能少的数字。例如,将(10,5)减少为(2,1),或作为另一个示例,将(144,60)减少为(12,5)。这样,总是存储最简单的数字。使用gcd(最大公约数)函数和simplifier函数可以做到这一点,这在代码中并不难理解。唯一的事情是该类面临一些带有负数的问题,因此它总是保存最终有理数是负数还是正数,如果它是负数,则使提名人成为负数。
初始化类的第二种方法是使用双精度型,并使用一个称为“ accuracy”的可选参数。该类将获取双精度值,以及所需小数点后多少数字的精度,并将双精度值转换为分母/分母形式,其中分母的幂为10。例如2.334将为2334/1000或342.57将为34257/100。然后尝试使用与#1方式相同的方法简化有理数。
类定义后,有类型别名'Rnl',您可以根据需要显然地对其进行更改。
然后有4个函数,用于数学的4个主要操作:* / +-,例如,您可以轻松地将Rational类型的两个数字相乘。
[之后,有2个对Rational类型的扩展,其中第一个('value')为您提供了Rational数的双精度值,第二个('rnlValue')为您提供了以人类形式的Rational数。可读的字符串:“分母/分母”
最后,您可以看到所有这些工作方式的一些示例。