作者:穿越时空lily | 来源:互联网 | 2022-12-02 15:29
我正在通过编写一个将美分价格转换成美元的程序来练习Haskell.显然,这意味着我的Int需要成为一个Double,但我希望我的最终输出是String的输出.我的代码:
首先,我定义了我的Price
类型:
type Price = Int
然后,我写了我的模块:
formatDollars :: Price -> String
formatDollars x = show (fromIntegral (x `div` 100))
我的测试输入x
是188,这应该给我1.88.
但我的输出总是如此"1"
.这让我觉得我的fromIntegral函数可能无法在我的代码中正确使用,就像它在小数点后截断一样.由于我对Haskell很新,我确信这是一个简单的错误; 有人可以帮帮我吗?
1> Mathematical..:
188 `div` 100 == 1
你想Double
在做分工之前转换成.(然后做真正的除法,而不是整数除法.)
formatDollars x = show ((fromIntegral x) / 100)
2> Willem Van O..:
div
是整数除法.这意味着div 188 100
,你得到:
Prelude> div 188 100
1
所以这意味着在之后div
,88
"走了".此外,div
截断到负无穷大.因此,如果数字是负数,我们得到:
Prelude> div (-210) 100
-3
但是你最好不要将数字转换为a Float
:Float
s不能精确地表示十进制数:使用近似值.例如,如果你写0.82
,将存储一个看起来像的值0.8200000000000001
(虽然这也不是确切的表示).如果数字相当大,则尾数并不总是能够表示整数,因此转换中可能存在错误.最后,如果您打印小数字或大数字,默认情况下show
将使用科学记数法(这当然不是"天生"的问题,但我认为如果您打印价格是相当奇怪的,我从来没有见过像这样的标签超市$ 9e-2
):
Prelude> 0.01 :: Float
1.0e-2
这可能不是你想要的.
您可以简单地将数字拆分为三个部分:小数点前的值和小数点后的两位数.喜欢:
import Data.Char(intToDigit)
formatDollars :: Price -> String
formatDollars pr = show pr0 ++ ['.', pr1, pr2]
where pr0 = quot pr 100
digit n = intToDigit (abs (rem n 10))
pr1 = digit (quot pr 10)
pr2 = digit pr
在Decimal
[hackage]包也可能是一种选择.A DecimalRaw
以这样的方式表示:对于十进制系统,它可以精确地表示数字.例如:
Prelude> import Data.Decimal
Prelude Data.Decimal> Decimal 2 188
1.88
所以我们可以避免所有的麻烦,并写如下:
import Data.Decimal(DecimalRaw(Decimal))
formatDollars :: Price -> String
formatDollars = show . Decimal 2