How to print integer literals in binary or hex in haskell?
HaskellBinaryIoHexHaskell Problem Overview
How to print integer literals in binary or hex in haskell?
printBinary 5 => "0101"
printHex 5 => "05"
Which libraries/functions allow this?
I came across the Numeric module and its showIntAtBase function but have been unable to use it correctly.
> :t showIntAtBase
showIntAtBase :: (Integral a) => a -> (Int -> Char) -> a -> String -> String
Haskell Solutions
Solution 1 - Haskell
The Numeric module includes several functions for showing an Integral type at various bases, including showIntAtBase
. Here are some examples of use:
import Numeric (showHex, showIntAtBase)
import Data.Char (intToDigit)
putStrLn $ showHex 12 "" -- prints "c"
putStrLn $ showIntAtBase 2 intToDigit 12 "" -- prints "1100"
Solution 2 - Haskell
You may also use printf of the printf package to format your output with c style format descriptors:
import Text.Printf
main = do
let i = 65535 :: Int
putStrLn $ printf "The value of %d in hex is: 0x%08x" i i
putStrLn $ printf "The html color code would be: #%06X" i
putStrLn $ printf "The value of %d in binary is: %b" i i
Output:
> The value of 65535 in hex is: 0x0000ffff
> The html color code would be: #00FFFF
> The value of 65535 in binary is: 1111111111111111
Solution 3 - Haskell
If you import the Numeric
and Data.Char
modules, you can do this:
showIntAtBase 2 intToDigit 10 "" => "1010"
showIntAtBase 16 intToDigit 1023 "" => "3ff"
This will work for any bases up to 16, since this is all that intToDigit
works for. The reason for the extra empty string argument in the examples above is that showIntAtBase
returns a function of type ShowS
, which will concatenate the display representation onto an existing string.
Solution 4 - Haskell
You can convert integer to binary with something like the following:
decToBin x = reverse $ decToBin' x
where
decToBin' 0 = []
decToBin' y = let (a,b) = quotRem y 2 in [b] ++ decToBin' a
usage in GHCi:
Prelude> decToBin 10
[1,0,1,0]
Solution 5 - Haskell
Hex can be written with 0x
and binary with 0b
prefix e.g.:
> 0xff
255
>:set -XBinaryLiterals
> 0b11
3
Note that binary requires the BinaryLiterals
extension.
Solution 6 - Haskell
You could define your own recursive functions like:
import Data.Char (digitToInt)
import Data.Char (intToDigit)
-- generic function from base to decimal
toNum :: [Char] -> Int -> (Char -> Int) -> Int
toNum [] base map = 0
toNum s base map = base * toNum (init(s)) base map + map(last(s))
-- generic function from decimal to base k
toKBaseNum :: Int -> Int -> (Int -> Char) -> [Char]
toKBaseNum x base map | x < base = [map x]
| otherwise = toKBaseNum (x `div` base) base map ++ [map(x `mod` base)]
-- mapping function for hex to decimal
mapHexToDec :: Char -> Int
mapHexToDec x | x == 'A' = 10
| x == 'B' = 11
| x == 'C' = 12
| x == 'D' = 13
| x == 'E' = 14
| x == 'F' = 15
| otherwise = digitToInt(x) :: Int
-- map decimal to hex
mapDecToHex :: Int -> Char
mapDecToHex x | x < 10 = intToDigit(x)
| x == 10 = 'A'
| x == 11 = 'B'
| x == 12 = 'C'
| x == 13 = 'D'
| x == 14 = 'E'
| x == 15 = 'F'
-- hex to decimal
hexToDec :: String -> Int
hexToDec [] = 0
hexToDec s = toNum s 16 mapHexToDec
-- binary to decimal
binToDec :: String -> Int
binToDec [] = 0
binToDec s = toNum s 2 (\x -> if x == '0' then 0 else 1)
-- decimal to binary
decToBin :: Int -> String
decToBin x = toKBaseNum x 2 (\x -> if x == 1 then '1' else '0')
-- decimal to hex
decToHex :: Int -> String
decToHex x = toKBaseNum x 16 mapDecToHex
Explanation: As you can see, the toNum function converts a k-based value to decimal, using the given base and a mapping function. The mapping function will map special characters to a decimal value (for ex. A=10, B=11, ... in hex). For binary mapping you could also use a lambda expression like you see in binToDec.
Whereas the toKBaseVal function is the opposite, converting a decimal to a k-based value. Again we need a mapping function which does the opposite: from a decimal to the corresponding special character of the k-based value.
As a test you can type:
binToDec(decToBin 7) = 7
Suppose you want to convert from decimal to octal:
-- decimal to octal
decToOct :: Int -> String
decToOct x = toKBaseNum x 8 (\x -> intToDigit(x))
Again, I use just a lambda expression, because the mapping is simple: just int to digit.
Hope that helps! Good programming!
Solution 7 - Haskell
Silly solution for one-liner fans:
(\d -> let fix f = let {x = f x} in x in fmap (\n -> "0123456789abcdef" !! n) (fix (\f l n -> if n == 0 then l :: [Int] else let (q, r) = quotRem n 16 in f (r:l) q) [] d)) 247
The nucleus of the one-liner is:
quotRem 247 16
For the sake of clarity, you can, alternatively, put the following in a file:
#!/usr/bin/env stack
{- stack script --resolver lts-12.1 -}
-- file: DecToHex.hs
module Main where
import System.Environment
fix :: (a -> a) -> a
fix f = let {x = f x} in x
ff :: ([Int] -> Int -> [Int]) -> [Int] -> Int -> [Int]
ff = \f l n ->
if n == 0
then l
else
let (q, r) = quotRem n 16
in f (r:l) q
decToHex :: Int -> String
decToHex d =
fmap (\n -> "0123456789abcdef" !! n)
(fix ff [] d)
main :: IO ()
main =
getArgs >>=
putStrLn . show . decToHex . read . head
And execute the script with:
stack runghc -- DecToHex.hs 247
I used fixed-point operator just so it is an example with fixed-point operator; also because it allowed me to construct the one-liner strictly bottom-up. (Note: bottom-up development is to be discouraged.)
References: stack script syntax, Command line arguments, fix
operator definition.
[tag:recursion] [tag:haskell] [tag:hex] [tag:haskell-stack]
Solution 8 - Haskell
Here is a simple, efficient, base-agnostic, Unlicenced implementation:
convertToBase :: Word8 -> Integer -> String
convertToBase b n
| n < 0 = '-' : convertToBase b (-n)
| n < fromIntegral b = [(['0'..'9'] ++ ['A' .. 'Z']) !! fromIntegral n]
| otherwise = let (d, m) = n `divMod` fromIntegral b in convertToBase b d ++ convertToBase b m
You have to import Data.Word
to use Word8
(which limits the values as much as reasonably possible), and you will often need fromIntegral
(if only automatic type conversions were a thing...).
Solution 9 - Haskell
Using the FiniteBits
class:
import Data.Bits (FiniteBits, finiteBitSize, testBit, shiftR)
showBits :: FiniteBits a => a -> String
showBits bits =
go (finiteBitSize bits - 1) where
go shift =
if shift >= 0
then
let bit = if testBit (shiftR bits shift) 0 then '1' else '0'
in bit : go (pred shift)
else
""
Examples:
showBits (4 :: Word8)
=> "00000100"
showBits (50 :: Int16)
=> "0000000000110010"
showBits (-127 :: Int32)
=> "11111111111111111111111110000001"
Solution 10 - Haskell
When working with text
, I recommend using the text-show
package which includes:
showbBin :: (Integral a, TextShow a) => a -> Builder
showbHex :: (Integral a, TextShow a) => a -> Builder
showbOct :: (Integral a, TextShow a) => a -> Builder
showbIntAtBase :: (Integral a, TextShow a) => a -> (Int -> Char) -> a -> Builder
For example, converting an Integer
to Text
in binary:
{-# LANGUAGE OverloadedStrings #-}
import TextShow (toText)
import TextShow.Data.Integral (showbBin)
toBinary :: Integer -> Text
toBinary n = toText . showbBin
> toBinary 6 == "110"
Perhaps you want to add a Text
prefix. Builder
allows you to efficiently construct Text
; it is a monoid.
toBinaryWithPrefix :: Text -> Integer -> Text
toBinaryWithPrefix prefix n = toText $ fromText prefix <> showbBin n
For more information see the TextShow
and TextShow.Data.Integral
modules available on Hackage.