Haskell-将整数转换为双精度,然后转换为字符串

时间:2018-10-11 08:08:45

标签: haskell type-conversion

我正在通过编写一个程序来练习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非常陌生,因此我敢肯定这是一个简单的错误;有人可以帮我吗?

2 个答案:

答案 0 :(得分:7)

188 `div` 100 == 1

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

formatDollars x = show ((fromIntegral x) / 100)

答案 1 :(得分:6)

div整数的除法。因此,这意味着div 188 100您将获得:

Prelude> div 188 100
1

因此,这意味着在div之后,88已“消失”。此外,div会截断为负无穷大。结果,如果数字为负,我们得到:

Prelude> div (-210) 100
-3

但是您最好还是不要将数字转换为FloatFloat不能精确表示十进制数字:使用了近似值。例如,如果您编写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]软件包也可能是一个选项。 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