4

I am trying to build a function that converts a Decimal(Int) into a Binary number. Unfortunately other than in java it is not possible to divide an int by two in haskell. I am very new to functional programming so the problem could be something trivial. So far I could not find another solution to this problem but here is my first try :

 fromDecimal :: Int -> [Int]

fromDecimal 0 = [0]
fromDecimal n = if (mod n 2 == 0) then 
                do

                0:fromDecimal(n/2) 

                else 
                do  
                1:fromDecimal(n/2) 

I got an java implementation here which I did before :

   public void fromDecimal(int decimal){
    for (int i=0;i<values.length;i++){

        if(decimal % 2 = 0)
        values[i]=true ; 
        decimal = decimal/ 2;
        else {values[i]= false;
        }       }
}

Hopefully this is going to help to find a solution!

4
  • 3
    Please don't use do's. dos are used for monads. Although lists are instances of the Monad typeclass, here these are not necessary. Another problem is that you use n/2 which means that n should be Fractional, to perform integer division, use div. Commented Jan 26, 2019 at 11:52
  • So this would be fine without do as well? Commented Jan 26, 2019 at 11:57
  • do for lists is OK. Not ok is using do for single expressions, it is totally redundant Commented Jan 26, 2019 at 11:59
  • 1
    Please don’t call it decimal-to-binary. There’s nothing decimal specific here. Commented Jan 26, 2019 at 12:53

5 Answers 5

10

There are some problems with your solution. First of all, I advise not to use do at all, until you understand what do does. Here we do not need do at all.

Unfortunately other than in java it is not possible to divide an int by two in haskell.

It actually is, but the / operator (which is in fact the (/) function), has type (/) :: Fractional a => a -> a -> a. An Int is not Fractional. You can perform integer division with div :: Integral a => a -> a -> a.

So then the code looks like:

fromDecimal :: Int -> [Int]
fromDecimal 0 = [0]
fromDecimal n = if (mod n 2 == 0) then 0:fromDecimal (div n 2) else 1:fromDecimal (div n 2)

But we can definitely make this more elegant. mod n 2 can only result in two outcomes: 0 and 1, and these are exactly the ones that we use at the left side of the (:) operator.

So we do not need to use an if-then-else at all:

fromDecimal :: Int -> [Int]
fromDecimal 0 = [0]
fromDecimal n = mod n 2 : fromDecimal (div n 2)

Likely this is still not exactly what you want: here we write the binary value such that the last element, is the most significant one. This function will add a tailing zero, which does not make a semantical difference (due to that order), but it is not elegant either.

We can define an function go that omits this zero, if the given value is not zero, like:

fromDecimal :: Int -> [Int]
fromDecimal 0 = [0]
fromDecimal n = go n
    where go 0 = []
          go k = mod k 2 : go (div k 2)

If we however want to write the most significant bit first (so in the same order as we write decimal numbers), then we have to reverse the outcome. We can do this by making use of an accumulator:

fromDecimal :: Int -> [Int]
fromDecimal 0 = [0]
fromDecimal n = go n []
    where go 0 r = r
          go k rs = go (div k 2) (mod k 2:rs)
Sign up to request clarification or add additional context in comments.

2 Comments

The result of the first and last function is 1,0,1,1,0 for the input 13 what is not 1101.
@disccco: for reasons mentioned in the answer: because your function (both in Java and Haskell if we correct it), produces a binary representation where the most significant bit is positioned as last element. The last solution however, reverses that (which is a more natural way to write it). But this is a bit related to big endian vs little endian.
4

You cannot / integers in Haskell – division is not defined in terms of integral numbers! For integral division use div function, but in your case more suitable would be divMod that comes with mod gratis.

Also, you are going to get reversed output, so you can reverse manually it after that, or use more memory-efficient version with accumulator:

decToBin :: Int -> [Int]
decToBin = go [] where
   go acc 0 = acc
   go acc n = let (d, m) = n `divMod` 2 in go (m : acc) d

go will give you an empty list for 0. You may add it manually if the list is empty:

decToBin = (\l -> if null l then [0] else l) . go [] where ...

Comments

2

Think through how your algorithm will work. It starts from 2⁰, so it will generate bits backward from how we ordinarily think of them, i.e., least-significant bit first. Your algorithm can represent non-negative binary integers only.

fromDecimal :: Int -> [Int]
fromDecimal d | d < 0     = error "Must be non-negative"
              | d == 0    = [0]
              | otherwise = reverse (go d)
  where go 0 = []
        go d = d `rem` 2 : go (d `div` 2)

In Haskell, when we generate a list in reverse, go ahead and do so but then reverse the result at the end. The reason for this is consing up a list (gluing new items at the head with :) has a constant cost and the reverse at the end has a linear cost — but appending with ++ has a quadratic cost.

Common Haskell style is to have a private inner loop named go that the outer function applies when it’s happy with its arguments. The base case is to terminate with the empty list when d reaches zero. Otherwise, we take the current remainder modulo 2 and then proceed with d halved and truncated.

Without the special case for zero, fromDecimal 0 would be the empty list rather than [0].

Comments

0

The binary numbers are usually strings and not really used in calculations. Strings are also less complicated.

The pattern of binary numbers is like any other. It repeats but at a faster clip. Only a small set is necessary to generate up to 256 (0-255) binary numbers. The pattern can systematically be expanded for more. The starting pattern is 4, 0-3

bd = ["00","01","10","11"]

The function to combine them into larger numbers is

d2b n = head.drop n $ [ d++e++f++g | d <- bd, e <- bd, f <- bd, g <- bd]

d2b 125

"01111101"

If it's not obvious how to expand, then

bd = ["000","001","010","011","100","101","110","111"]

Will give you up to 4096 binary digits (0-4095). All else stays the same.

If it's not obvious, the db2 function uses 4 pairs of binary numbers so 4 of the set. (2^8) - 1 or (2^12) - 1 is how many you get.

By the way, list comprehension are sugar coated do structures.

Generate the above patterns with

[ a++b | a <- ["0","1"], b <- ["0","1"] ]

["00","01","10","11"]

and

[ a++b++c | a <- ["0","1"], b <- ["0","1"], c <- ["0","1"] ]

["000","001","010","011","100","101","110","111"]

More generally, one pattern and one function may serve the purpose

b2 = ["0","1"]
b4 = [ a++b++c++d | a <- b2, b <- b2, c <- b2, d <- b2]
b4

["0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"]

bb n = head.drop n $ [ a++b++c++d | a <- b4, b <- b4, c <- b4, d <- b4]
bb 32768

"1000000000000000"

bb 65535

"1111111111111111"

To calculate binary from decimal directly in Haskell using subtraction

cvtd n (x:xs) | x>n  = 0:(cvtd  n xs)
              | n>x  = 1:(cvtd (n-x) xs)
              | True = 1:[0|f<-xs]

Use any number of bits you want, for example 10 bits.

cvtd 639 [2^e|e<-[9,8..0]]

[1,0,0,1,1,1,1,1,1,1]

Comments

0
import Data.List


dec2bin x =
  reverse $ binstr $ unfoldr ndiv x
  where
    binstr = map (\x -> "01" !! x)
    exch (a,b) = (b,a)
    ndiv n =
      case n of
        0 -> Nothing
        _ -> Just $ exch $ divMod n 2

3 Comments

Welcome to Stack Overflow. It would be helpful if you could explain why your code would solve the problem.
Hi, @AdrianMole! I use divMod with unfoldr (from Data.List) for get sequence mods from divide begin value on 2, binstr for convert number to char and reverse for get correct result (from beginning he getting as backward).
For example: divMod 125 2 -> (62,1) divMod 62 2 -> (31,0) divMod 31 2 -> (15,1) divMod 15 2 -> (7,1) divMod 7 2 -> (3,1) divMod 3 2 -> (1,1) divMod 1 2 -> (0,1) 0 as answer - > exit from cycle. For cycle creat i use function unfoldr which decompose begin value (See unfoldr in Data.List).

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.