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

Haskell-将整数转换为双精度,然后转换为字符串,haskell,type-conversion,Haskell,Type Conversion,我正在通过编写一个程序来练习Haskell,该程序将以美分为单位的价格转换为美元。显然,这意味着我的Int需要变成一个Double,但我希望我的最终输出是一个字符串。我的代码: 首先,我定义了我的Price类型: type Price=Int 然后,我编写了我的模块: formatDollars :: Price -> String formatDollars x = show (fromIntegral (x `div` 100)) 我对x的测试输入是188,应该是1.88 但是我的输

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

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

type Price=Int

然后,我编写了我的模块:

formatDollars :: Price -> String
formatDollars x = show (fromIntegral (x `div` 100))
我对
x
的测试输入是188,应该是1.88


但是我的输出总是
“1”
。这让我觉得我的fromIntegral函数在我的代码中可能没有正确使用,就好像它在小数点后截断一样。因为我是哈斯克尔的新手,我相信这是一个简单的错误;有人能帮我吗?

div
是整数除法。这意味着
div188100
,您可以得到:

Prelude> div 188 100
1
这意味着在
div
之后,
88
就“消失了”。此外,
div
截断为负无穷大。因此,如果数字为负数,我们得到:

Prelude> div (-210) 100
-3
但您可能最好不要将数字转换为
Float
无论如何:
Float
s无法精确表示十进制数字:使用近似值。例如,如果您写入
0.82
,将存储一个看起来像
0.8200000000000001
(尽管这也不是精确的表示)。如果数字相当大,尾数并不总是能够表示整个数字,因此在转换过程中可能会出现错误。最后,如果你打印小的或大的数字,默认情况下,
show
将使用科学记数法(这当然不是“天生”的问题,但我认为如果你打印一个价格,这是相当奇怪的,我从来没有见过超市有像
$9e-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
包也可能是一个选项。十进制RAW的表示方式是,对于十进制系统,它可以精确地表示数字。例如:

Prelude> import Data.Decimal
Prelude Data.Decimal> Decimal 2 188
1.88 
因此,我们可以避免所有的麻烦,并这样写:

import Data.Decimal(DecimalRaw(Decimal))

formatDollars :: Price -> String
formatDollars = show . Decimal 2
在进行除法之前,您希望转换为
Double
。(然后进行实除法,而不是整数除法。)


div
是整数除法,因此从那一点开始,该值就消失了。它应该是一个
双精度的
;事实上,您可能应该使用。我相信在
Numeric
(?)中也有一些数字格式的东西…@MathematicalOrchid:是的,像
showHFloat
,但我并不真的喜欢这里的
RealFoat
约束,因为从那时起我们又进入了“浮点”世界。特别是对于价格,使用浮点计算可能导致将值相加为不完全正确的值。
import Data.Decimal(DecimalRaw(Decimal))

formatDollars :: Price -> String
formatDollars = show . Decimal 2
188 `div` 100 == 1
formatDollars x = show ((fromIntegral x) / 100)