我正在构建一个计算器,希望它能自动将每个小数转换成分数。因此,如果用户计算一个表达式,其答案是“0.333333...",它将返回“1/3”。对于“0.25”,它将返回“1/4”。使用GCD,如这里所示(Decimal to fraction conversion),我已经找到了如何将任意有理数的终止小数转换为小数,但这对任何重复的小数(如.333333)都不起作用。
所有其他的堆栈溢出函数都是Objective-C的,但是我需要在我的swift应用程序中有一个函数!所以这个函数的翻译版本(https://stackoverflow.com/a/13430237/5700898)会很好!
任何关于如何将有理数或重复/无理小数转换为分数(即将“0.1764705882...”转换为3/17)的想法或解决方案都将是伟大的!
3条答案
按热度按时间nhaq1z211#
如果你想将计算结果显示为有理数,那么唯一100%正确的解决方案是在所有计算中使用 * 有理算术 *,即所有中间值都存储为一对整数
(numerator, denominator)
,所有加法、乘法、除法等都使用有理数的规则来完成。一旦将结果赋给一个 * 二进制浮点数 *(如
Double
),信息就会丢失。例如,在
x
中存储0.7
的 * 近似值 *,因为该数不能精确地表示为Double
。From可以看到
x
保持该值因此,
x
作为一个有理数的正确表示应该是6305039478318694 / 9007199254740992
,但这当然不是你所期望的,你所期望的是7/10
,但还有一个问题:会将完全相同的值指派给
x
,因此在Double
的精确度内,无法将它与0.7
区分。那么
x
应该显示为7/10
还是69999999999999996/100000000000000000
呢?如上所述,使用有理数算法是最好的解决方案。如果这不可行,那么你可以将
Double
转换回具有给定精度 * 的有理数 *。(以下内容摘自Swift中的双精度LCM算法。)Continued Fractions是一种有效的方法,可以创建一个(有限或无限)分数序列 hn/kn,这些分数是给定真实的 x 的任意良好近似,下面是Swift中的一个可能实现:
示例:
默认精度为1.0E-6,但您可以根据需要进行调整:
Swift 3版本:
示例:
或者,如@brandonscript所建议的,使用
struct Rational
和初始化器:示例用法:
8oomwypt2#
虽然有点晚了,但我也遇到了类似的问题,最后还是构建了Swift FractionFormatter。这是因为你关心的大部分无理数都是普通分数的一部分,很容易验证正确的变换。其余的可能是四舍五入,也可能不是。但是您可以非常接近用户可能生成的任何合理的分数。它被设计为NumberFormatter的替代品。
fae0ux8s3#
正如Martin R所说,要得到(99.99%)精确的计算,唯一的方法就是从头到尾用有理数计算一切。
创建这个类的原因还在于我需要非常精确的计算,而这在swift提供的类型中是不可能的。所以我创建了自己的类型。
这是代码,我将在下面解释它。
首先,我们有类本身。类可以用两种方式初始化:
在Rational类中,alpha ~=分母,beta ~=分母
第一种方法是使用两个整数初始化类,第一个是分母,第二个是分母。类得到这两个整数,然后将它们减少到可能的最小数。(10,5)至(2,1)或作为另一个例子,减少了(144、60)至(12,5)。这样,总是存储最简单的数。这可以使用gcd来实现。(最大公约数)函数和简化器函数,这些从代码中并不难理解。唯一的问题是该类面临一些负数的问题,所以它总是保存最终有理数是负的还是正的,如果它是否定的,它使主词是否定的。
第二种初始化类的方法是使用一个双精度型和一个名为“accuracy”的可选参数。该类获取双精度型,以及您需要的小数点后数字的精度,并将双精度型转换为分母/分母形式。其中分母是10的幂。例如,2.334将是2334/1000或342.57将是34257/100。然后尝试使用在#1方式中解释的相同方法来简化有理数。
在类定义之后,有一个类型别名“Rnl”,显然您可以根据需要对其进行更改。
然后有4个函数,用于数学的4个主要操作:* / + -,我定义了它,例如,你可以很容易地将两个有理数相乘。
在此之后,有两个对Rational类型的扩展,其中第一个('value')为您提供了有理数的双精度值,第二个('rnlValue')为您提供了可读字符串形式的有理数:“分子/分母”
最后,您可以看到一些示例来说明所有这些方法是如何工作的。