3

我正在通过编写一个将美分价格转换为美元的程序来练习 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 还很陌生,我确信这是一个简单的错误;有人可以帮帮我吗?

4

2 回答 2

7
188 `div` 100 == 1

您想在进行除法Double 之前转换为。(然后进行实数除法,而不是整数除法。)

formatDollars x = show ((fromIntegral x) / 100)
于 2018-10-11T08:23:42.003 回答
6

div整数除法。所以这意味着div 188 100,你得到:

Prelude> div 188 100
1

所以这意味着在 之后div88是“走了”。此外div截断为负无穷大。结果,如果数字为负,我们得到:

Prelude> div (-210) 100
-3

但是您可能最好不要将数字转换为 a FloatFloats 无法精确表示十进制数:使用近似值。例如,如果您编写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

[ Decimalhackage]包也可能是一个选项。ADecimalRaw的表示方式是,对于十进制系统,它可以精确地表示数字。例如:

Prelude> import Data.Decimal
Prelude Data.Decimal> Decimal 2 188
1.88 

所以我们可以避免所有的麻烦,写成这样:

import Data.Decimal(DecimalRaw(Decimal))

formatDollars :: Price -> String
formatDollars = show . Decimal 2
于 2018-10-11T08:23:37.497 回答