我想编写一个函数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?。但由于我输入了这么多,我想发布它。至少上下文不同。
答案 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)