Haskell: how to properly display a percentage up to two decimals?
Here's a function to display a floating point number used to represent a percentage. It doesn't include the %
symbol, but you could prepend that. It's validating the number is between 0
and 100
because this function would produce incorrect results for a number like 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
Results:
λ 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
printf
is solid, and another library worth knowing about is formatting (which is based off of the lovely HoleyMonoid library):
Prelude Formatting> format ("to two decimal places: " % prec 2 % "!") 0.2222
"to two decimal places: 0.22!"
Note that formatting
is type-safe, unlike 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
> import Text.Printf
> printf "%.2f" 0.22324 :: String
"0.22"
You can use most format strings C's printf
supports.
Keep in mind, however, that Haskell's printf
involves some complex typeclass machinery, and can generate hard-to-read type errors. It is also very general, since it can also return IO actions, i.e.
> printf "%.2f" 0.22324 :: IO ()
0.22
does not return a string but directly prints it. I'd recommend you always add a type annotation (such as :: String
above) after each call of printf
, unless it's clear from the context what is the return type (e.g. in a do
block with other IO actions).