73

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

10 Answers 10

103

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"
Sign up to request clarification or add additional context in comments.

5 Comments

For anyone lazy like me who didn't scroll down, the printf example is much more concise and flexible, and can do other useful stuff like e.g. give a constant length string and all other printf features. Instead of above, just: printf "%032b" 5
@mozboz, printf in Haskell is more like a magic trick than a function to be used in serious code. The format string is parsed at runtime (which may produce runtime errors) and the whole mechanism is a bit slow.
It is a shame that there is no variant of showIntAtBase that pads the output with zeroes. After all, if you're formatting as binary or hexadecimal, it is unlike that you want to drop all the leading zeros.
@AndrewThaddeusMartin Since showIntAtBase works for Integer as well, it couldn't pad automatically, since the bit-length is variable. So you'd have to specify the width to pad to, and then it's getting to where it's just as easy to do it yourself, with a general padding function (where you can pick the padding character--zeros or spaces or whatever).
For anyone looking for variable width, and because the official docs have zero examples, printf "%0*b" n 3 would use width n for the boolean representation of 3.
42

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

3 Comments

` printf "The value of %d in hex is: 0x%08x" i i ` is ok because printf can be both IO () and String
Yes, indeed. I just wanted to make it obvious, that it can be used as a pure function that returns a String.
For the record, Text.Printf is in base now.
30

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.

Comments

8

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]

2 Comments

Would it not be better to have b:(decToBIn a) at the end of the last line?
Shouldn't 0 give back 0? decToBin' 0 = [0] maybe?
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.

Comments

3

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!

Comments

3

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.

1 Comment

I think you can use ("0123456789abcdef" !!) instead of (\n -> "0123456789abcdef" !! n).
1

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...).

2 Comments

All the other answers ignore Data.Word. And lets get real here - once you do "bit hammering", e.g. using Data.Bits, you are more likely to want to see hex or binary numbers AND you are more likely not working with Int but much rather with Word8, Word16, ... +1
although it is a curious choice he made regarding using Word8 for the base instead of for the number... Could be improved... Even better if it worked for all Bits a.
0

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"

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.