Swift中的十进制到小数转换

问题描述 投票:7回答:2

我正在构建一个计算器,并希望它自动将每个小数转换为小数。因此,如果用户计算出答案为“ 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的任何想法或解决方案都不错!

ios swift swift2 calculator fractions
2个回答
38
投票

如果要将计算结果显示为有理数那么唯一的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)


0
投票
正如Martin R所说,进行精确计算(99.99%)的唯一方法是从开始到结束都用有理数计算所有内容。

之所以创建该类,也是因为我需要进行非常精确的计算,而对于迅速提供的类型,这是不可能的。所以我创建了自己的类型。

这是代码,我将在下面解释。

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数。可读的字符串:“分母/分母”

最后,您可以看到所有这些工作方式的一些示例。

© www.soinside.com 2019 - 2024. All rights reserved.