【问题标题】:Printing floating point numbers in Haskell在 Haskell 中打印浮点数
【发布时间】:2016-03-28 18:40:38
【问题描述】:

我在 Haskell 中有一个看起来像这样的函数

type Price    = Int

formatPence :: Price -> String
formatPence a = show (a `div` 100) ++ "." ++ show(a `mod` 100)

因此,例如,如果我输入formatPence 1023,则输出将为"10.23"。但是如果我输入1202 会出现问题,因为输出将是"12.2"。我应该添加什么?谢谢:)

【问题讨论】:

    标签: haskell show floating


    【解决方案1】:

    也许您想要Numeric 中的各种show*Float 函数之一?

    > :m Numeric
    > showFFloat (Just 2) 123.456 ""
    123.45
    

    【讨论】:

    • 这没有回答问题,他想要一个类型为“Float -> String”的函数,这避免了使该解决方案无用的步骤......如果他想打印到文件怎么办。
    • 对于这样的函数,只要抽象出float即可:(\f -> showFFloat (Just 2) f "") :: Float -> String
    【解决方案2】:

    这是人们自计算机诞生以来就遇到的标准问题。因此,古老的printf(Haskell 或多或少地从 C 复制而来)完美地解决了这个问题。

    import Text.Printf
    
    formatPence = printf "%.2f" . (/100) . fromIntegral
    

    哦,要注意...这对于非常大的数量有一个精度问题,因为Double(隐式用于除法)没有Int 那样高分辨率。

    Prelude Text.Printf> formatPence 10000000000000013
    "100000000000000.13"
    Prelude Text.Printf> formatPence 100000000000000013
    "1000000000000000.10"
    Prelude Text.Printf> formatPence 1000000000000000013
    "10000000000000000.00"
    

    因此,如果您要处理数万亿美元的金额,最好不要使用它。

    (我想如果你处理的是这样的金额,你可能不会在这里问这个问题......你也不会使用Int。)

    要解决此问题,您可以使用原始方法,但仍使用 printf 格式化多余的零:

    type Price' = Integer
    
    formatPence' :: Price' -> String
    formatPence' a = show (a `div` 100) ++ "." ++ printf "%02u" (a `mod` 100) 
    

    这将适用于任意可笑的金额:

    > formatPence' 1000000000000000103
    "10000000000000001.03"
    > formatPence' 6529857623987620347562395876204395876395762398417639852764958726398527634972365928376529384
    "65298576239876203475623958762043958763957623984176398527649587263985276349723659283765293.84"
    

    请注意,手动 div/mod 会导致出现负数问题,但这很容易解决。

    【讨论】:

    • @SitiAisyah, 打电话给printf 一个完美的解决任何事情的方法都太过分了。我认为formatting package 为C 的printf 提供了更好的替代品。它避免了printf 容易出错的运行时格式字符串处理废话,转而采用强类型方法;不利的一面是,当您搞砸时,您可能会从编译器收到难以理解的错误消息。我个人很欣赏一大堆真正惯用的、命名良好的 blah -> String 函数,但我不确定是否有人拥有。
    • 同意。我不是 printf 的伟大支持者,它具有奇怪的可变参数签名,基本上只在运行时进行类型检查;但对于单个数字的简单格式,它就足够了,它在base,所以,对于这个特殊的任务,我推荐它。
    【解决方案3】:

    小数点后的部分需要补零。

    twoDP :: Int -> String
    twoDP v = if length str == 1 then '0':str else str
       where str = show v
    

    你可以写一个更通用的填充函数:

    leadingZeros :: Int -> Int -> String
    leadingZeros n v = replicate (n - length str) '0' ++ str
       where str = show v
    

    不要为这种事情弄乱浮点版本的“show”或“printf”:那样会很疯狂。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-06
      • 2013-05-12
      相关资源
      最近更新 更多