在Haskell中计算适当的分数

时间:2011-09-05 03:33:05

标签: haskell

我想编写一个函数makeFraction :: Float -> Float -> (Int, Int),每当我说makeFraction a b时,它返回(x,y),使得x / y是等于a / b的适当分数。例如,makeFraction 17.69 5.51应返回(61,19)。

我有一个子程序来计算两个数字的gcd,但我的第一个任务是将a和b转换为Int,例如应将17.69和5.51转换为1769和551。

现在我想对任意小数位的数字进行处理。 Prelude功能对我帮助不大。例如,当我说toFraction(0.2)时;它会返回3602879701896397 % 18014398509481984,这会严重影响我后来计算的正确性。

后来我尝试通过使用另一个库函数properFraction(17.69)来获取小数值,它假设只给我0.69但它产生0.69000 ... 013这不是我会接受的正确心态。

它确实看起来像浮点算术引起的问题。直到现在我没有进行任何数据操作,只是要求存储位的一部分,我应该能够从处理器寄存器/存储器位置获取。 Haskell中有没有特殊的函数库来执行这样的任务?

PS:好像这里有一些有用的提示How to parse a decimal fraction into Rational in Haskell?。但由于我输入了这么多,我想发布它。至少上下文不同。

4 个答案:

答案 0 :(得分:8)

是的,这是您遇到的浮点运算的有限精度。浮点格式不能精确地表示0.2,因此toFraction实际上为您提供了当您要求0.2时获得的Float数的精确理性值。

类似地,17.69无法准确表示,并且因为点浮动,其最佳表示的绝对误差大于表示0.69中的误差。因此,当你取走整数部分时,结果位不一样就像你要求从一开始就尽可能好地表示0.69一样,当实现打印时可以看到这种差异以十进制形式输出结果。

答案 1 :(得分:4)

在我看来,不是使用像Float或Double这样的浮点类型,而是应该使用可以完全表示这些数字的类型来完成所有计算,例如Rational。例如,

(17.69 :: Rational) / (5.51 :: Rational)

评估为61 % 19

答案 2 :(得分:1)

如其他答案所述,Float不一定能准确表示给定的十进制数。特别是,Float在内部使用a/(2^m)形式存储。因此,像3/10这样的实数只能用浮点数近似。

但如果你需要一个合适的近似值,这可能有所帮助:

import Data.Ratio

convertFloat :: Float -> Rational
convertFloat f = let
        denom = 10^6
        num = fromInteger denom * f
        in round num % denom

例如:

> convertFloat 17.69
1769 % 100
> convertFloat 17.69 / convertFloat 5.51
61 % 19

答案 3 :(得分:0)

查看base的{​​{3}}模块,尤其是Numeric功能。

> floatToDigits 10 17.69
([1,7,6,9],2)