'How to print integer literals in binary or hex in haskell?

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


Solution 1:[1]

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 2:[2]

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 3:[3]

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 4:[4]

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 5:[5]

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 6:[6]

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.

Solution 7:[7]

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 8:[8]

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 9:[9]

When working with text, I recommend using the text-show package which includes:

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ntc2
Solution 2 Ian Ross
Solution 3 stites
Solution 4 Mikkel
Solution 5 Alessandro Giusa
Solution 6
Solution 7
Solution 8
Solution 9 dopamane