Haskell: 如何正确显示最多两位小数的百分比?
Haskell: how to properly display a percentage up to two decimals?
在一个程序中,我计算一个百分比并将它打印给用户。问题是打印的百分比显示有太多小数。我搜索了一个解决这个问题的函数,没有找到,并在下面编写了舍入函数,但我想知道是否有更标准的方法来代替。此外,this Java topic 中描述的想法非常简洁,如果已经有函数可以做到这一点,我很想知道。
roundTo :: (Integral int1, Integral int2) => int1 -> Double -> Either int2 Double
roundTo digitsAfterComma value
| (digitsAfterComma >= 1) =
let
factor = fromIntegral (10 ^ digitsAfterComma)
result = ((/ factor) . fromIntegral . round . (* factor)) value
in
Right result
| (digitsAfterComma == 0) = Left (round value)
| otherwise = error "minimal precision must be non-negative"
更新:更完整的版本如下,开发感谢我收到的一些答案。
import qualified Text.Printf as T
showPercentage :: (Integral a, Show a) => a -> Double -> String
showPercentage digitsAfterComma fraction
| (digitsAfterComma >= 1) =
let formatString = "%." ++ (show digitsAfterComma) ++ "f%%"
in T.printf formatString percentage
| (digitsAfterComma == 0) =
let formatString = "%d%%"
in T.printf formatString (round percentage :: Int)
| otherwise = error "minimal precision must be non-negative"
where percentage = 100 * fraction
> import Text.Printf
> printf "%.2f" 0.22324 :: String
"0.22"
您可以使用 most format strings C's printf
supports.
但是请记住,Haskell 的 printf
涉及一些复杂的类型类机制,并且会产生难以阅读的类型错误。它也很通用,因为它也可以 return IO 操作,即
> printf "%.2f" 0.22324 :: IO ()
0.22
不是 return 字符串而是直接打印它。我建议你总是在每次调用 printf
之后添加类型注释(例如上面的 :: String
),除非从上下文中可以清楚地知道 return 类型是什么(例如在 do
与其他 IO 操作一起阻塞)。
printf
是可靠的,另一个值得了解的库是 formatting (which is based off of the lovely HoleyMonoid 库):
Prelude Formatting> format ("to two decimal places: " % prec 2 % "!") 0.2222
"to two decimal places: 0.22!"
请注意 formatting
是类型安全的,不同于 printf
:
Prelude Text.Printf Formatting> :t printf "%.2f" "hi"
printf "%.2f" "hi" :: PrintfType t => t
Prelude Text.Printf Formatting> printf "%.2f" "hi"
*** Exception: printf: bad formatting char 'f'
Prelude Text.Printf Formatting> :t format (prec 2) "hi"
-- type error
这是一个显示用于表示百分比的浮点数的函数。它不包含 %
符号,但您可以在其前面加上。它正在验证数字在 0
和 100
之间,因为此函数会为 1e7
.
这样的数字产生不正确的结果
showPercent :: Double -> Maybe Text
showPercent d
| d < 0 = Nothing
| d > 100 = Nothing
| otherwise = Just . pack $ f d
where
f x = reverse $ case reverse (show x) of
'0':'.':rest -> rest
rest -> rest
结果:
λ showPercent (-5)
Nothing
λ showPercent 0
Just "0"
λ showPercent 5
Just "5"
λ showPercent 10.5
Just "10.5"
λ showPercent 15.765
Just "15.765"
λ showPercent 35.123000000
Just "35.123"
λ showPercent 51.123000321000
Just "51.123000321"
λ showPercent 200
Nothing
在一个程序中,我计算一个百分比并将它打印给用户。问题是打印的百分比显示有太多小数。我搜索了一个解决这个问题的函数,没有找到,并在下面编写了舍入函数,但我想知道是否有更标准的方法来代替。此外,this Java topic 中描述的想法非常简洁,如果已经有函数可以做到这一点,我很想知道。
roundTo :: (Integral int1, Integral int2) => int1 -> Double -> Either int2 Double
roundTo digitsAfterComma value
| (digitsAfterComma >= 1) =
let
factor = fromIntegral (10 ^ digitsAfterComma)
result = ((/ factor) . fromIntegral . round . (* factor)) value
in
Right result
| (digitsAfterComma == 0) = Left (round value)
| otherwise = error "minimal precision must be non-negative"
更新:更完整的版本如下,开发感谢我收到的一些答案。
import qualified Text.Printf as T
showPercentage :: (Integral a, Show a) => a -> Double -> String
showPercentage digitsAfterComma fraction
| (digitsAfterComma >= 1) =
let formatString = "%." ++ (show digitsAfterComma) ++ "f%%"
in T.printf formatString percentage
| (digitsAfterComma == 0) =
let formatString = "%d%%"
in T.printf formatString (round percentage :: Int)
| otherwise = error "minimal precision must be non-negative"
where percentage = 100 * fraction
> import Text.Printf
> printf "%.2f" 0.22324 :: String
"0.22"
您可以使用 most format strings C's printf
supports.
但是请记住,Haskell 的 printf
涉及一些复杂的类型类机制,并且会产生难以阅读的类型错误。它也很通用,因为它也可以 return IO 操作,即
> printf "%.2f" 0.22324 :: IO ()
0.22
不是 return 字符串而是直接打印它。我建议你总是在每次调用 printf
之后添加类型注释(例如上面的 :: String
),除非从上下文中可以清楚地知道 return 类型是什么(例如在 do
与其他 IO 操作一起阻塞)。
printf
是可靠的,另一个值得了解的库是 formatting (which is based off of the lovely HoleyMonoid 库):
Prelude Formatting> format ("to two decimal places: " % prec 2 % "!") 0.2222
"to two decimal places: 0.22!"
请注意 formatting
是类型安全的,不同于 printf
:
Prelude Text.Printf Formatting> :t printf "%.2f" "hi"
printf "%.2f" "hi" :: PrintfType t => t
Prelude Text.Printf Formatting> printf "%.2f" "hi"
*** Exception: printf: bad formatting char 'f'
Prelude Text.Printf Formatting> :t format (prec 2) "hi"
-- type error
这是一个显示用于表示百分比的浮点数的函数。它不包含 %
符号,但您可以在其前面加上。它正在验证数字在 0
和 100
之间,因为此函数会为 1e7
.
showPercent :: Double -> Maybe Text
showPercent d
| d < 0 = Nothing
| d > 100 = Nothing
| otherwise = Just . pack $ f d
where
f x = reverse $ case reverse (show x) of
'0':'.':rest -> rest
rest -> rest
结果:
λ showPercent (-5)
Nothing
λ showPercent 0
Just "0"
λ showPercent 5
Just "5"
λ showPercent 10.5
Just "10.5"
λ showPercent 15.765
Just "15.765"
λ showPercent 35.123000000
Just "35.123"
λ showPercent 51.123000321000
Just "51.123000321"
λ showPercent 200
Nothing