Haskell - 将整数转换为double,然后转换为String

gbm*_*102 3 haskell type-conversion

我正在通过编写一个将美分价格转换成美元的程序来练习Haskell.显然,这意味着我的Int需要成为一个Double,但我希望我的最终输出是String的输出.我的代码:

首先,我定义了我的Price类型:

type Price = Int

然后,我写了我的模块:

formatDollars :: Price -> String
formatDollars x = show (fromIntegral (x `div` 100))
Run Code Online (Sandbox Code Playgroud)

我的测试输入x是188,这应该给我1.88.

但我的输出总是如此"1".这让我觉得我的fromIntegral函数可能无法在我的代码中正确使用,就像它在小数点后截断一样.由于我对Haskell很新,我确信这是一个简单的错误; 有人可以帮帮我吗?

Mat*_*hid 7

188 `div` 100 == 1
Run Code Online (Sandbox Code Playgroud)

你想Double 在做分工之前转换成.(然后做真正的除法,而不是整数除法.)

formatDollars x = show ((fromIntegral x) / 100)
Run Code Online (Sandbox Code Playgroud)


Wil*_*sem 6

div是整数除法.这意味着div 188 100,你得到:

Prelude> div 188 100
1
Run Code Online (Sandbox Code Playgroud)

所以这意味着在之后div,88"走了".此外,div截断到负无穷大.因此,如果数字是负数,我们得到:

Prelude> div (-210) 100
-3
Run Code Online (Sandbox Code Playgroud)

但是你最好不要将数字转换为a Float:Floats不能精确地表示十进制数:使用近似值.例如,如果你写0.82,将存储一个看起来像的值0.8200000000000001(虽然这也不是确切的表示).如果数字相当大,则尾数并不总是能够表示整数,因此转换中可能存在错误.最后,如果您打印小数字或大数字,默认情况下show将使用科学记数法(这当然不是"天生"的问题,但我认为如果您打印价格是相当奇怪的,我从来没有见过像这样的标签超市$ 9e-2):

Prelude> 0.01 :: Float
1.0e-2
Run Code Online (Sandbox Code Playgroud)

这可能不是你想要的.

您可以简单地将数字拆分为三个部分:小数点前的值和小数点后的两位数.喜欢:

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
Run Code Online (Sandbox Code Playgroud)

在Decimal[hackage]包也可能是一种选择.A DecimalRaw以这样的方式表示:对于十进制系统,它可以精确地表示数字.例如:

Prelude> import Data.Decimal
Prelude Data.Decimal> Decimal 2 188
1.88 
Run Code Online (Sandbox Code Playgroud)

所以我们可以避免所有的麻烦,并写如下:

import Data.Decimal(DecimalRaw(Decimal))

formatDollars :: Price -> String
formatDollars = show . Decimal 2
Run Code Online (Sandbox Code Playgroud)