In a program, I calculate a percentage and print it to the user. The issue is that the percentage printed is shown with too many decimals. I searched for a function addressing this issue, didn't find one, and programmed the rounding function below, but I wonder if there is a more standard way to do this instead. Also, the idea described in this Java topic is pretty neat, and if there already are functions to do that, I would love to know.
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"
Update: A more complete version follows, developed thanks to some of the answers I've received.
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"
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).
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With