3

I am trying to build a standard function to convert any decimal value to its octal, hexadecimal and binary equivalent but it doesn't work for binary for some reason. I tried putting extra precautions into the conditional statement to check for base 2 but it still did not work.

This is the function (I know that this doesn't work well with hexadecimal values. I am going to take care of that afterwards):

def convert(num, base):
    remainder = num % base
    conv = []

    if(remainder == 0):
        conv.append('0')
    elif(remainder != 0 and base == 2):
        conv.append('1')
    else:
        conv.append(str(remainder))

    result = ''.join(conv)
    output = result[::-1]

    return int(output)

In the line elif(remainder != 0 and base == 2):, I am checking if the remainder isn't 0 and the base is 2 to add a 1 into the temporary conv list. I am then converting the list to a string, reversing it and returning it as an int.

For example. If the input is 17, the output needs to be this:

    1     1     1     1
    2     2     2    10
    3     3     3    11
    4     4     4   100
    5     5     5   101
    6     6     6   110
    7     7     7   111
    8    10     8  1000
    9    11     9  1001
   10    12     A  1010
   11    13     B  1011
   12    14     C  1100
   13    15     D  1101
   14    16     E  1110
   15    17     F  1111
   16    20    10 10000
   17    21    11 10001

These are the functions that take care of the input and the printing:

def print_formatted(number):
    # your code goes here

    for i in range(number):
        print(
            str(i + 1) + " " + 
            str(convert(i + 1, 8)) + " " + 
            str(convert(i + 1, 16)) + " " + 
            str((convert(i + 1, 2)))
            )


if __name__ == '__main__':
    n = int(input())
    print_formatted(n)

Update

Instead of going through the entire equation, I decided to go with the built-in functions and trim the first two characters (i.e 0b) so it fits the format well. I am trying to space them away from each other based on the width of the binary output but I couldn't figure out a way of doing that. This is what I have so far:

def convert(num, base):
    # get the highest power
    val = ''
    hex_char_list = ['A', 'B', 'C', 'D', 'E', 'F']

    if(base == 2):
        bin_num = bin(num)
        bin_list = list(bin_num)
        bin_list_2 = bin_list[2:]
        val = ''.join(bin_list_2)

    if(base == 8):
        oct_num = oct(num)
        oct_list = list(oct_num)
        oct_list_2 = oct_list[2:]
        val = ''.join(oct_list_2)

    if(base == 16):
        hex_num = hex(num)
        hex_list = list(hex_num)
        hex_list_2 = hex_list[2:]
        val = ''.join(hex_list_2)

        if val in hex_char_list:
            val = val.upper()

    return val

def print_formatted(number):
    # your code goes here
    width = len(convert(number, 2).format(number))

    for i in range(number):
        print(
            str(i + 1) + width + 
            str(convert(i + 1, 8)) + width + 
            str(convert(i + 1, 16)) + width + 
            str((convert(i + 1, 2)))
            )


if __name__ == '__main__':
    n = int(input())
    print_formatted(n)
Onur-Andros Ozbek
  • 2,998
  • 2
  • 29
  • 78

2 Answers2

3

Your elif is superflous - if you do %2 the result can be only 0 or 1 - no need to do handle it differently.


Your code does not convert the whole number - you check the modulo of the number, not how often your base (and higher powers of your base) fits into it.

You need to get the highest possible power for your base that fits into your number. Then you need to get how often that one fits into your number, subtract it from the number and continue with the remainder of that operation. You shrink your power by one, and continue until your num is 0. Then you accumulate all numbers into a string.

Your code fixed:

def convert(num, base):
    # get the highest power
    power = 0
    while num // (base**(power+1)) > 0:
        power += 1

    # divide, remember, subtract - until down to the lowest power
    result = []
    while num >= 0:
        p = base**power
        if p == 1:
            result.append(num)
            break
        result.append(num // p)
        num -= result[-1]*p
        power -= 1

    return ''.join(map(str,result))

to get an output of:

1 1 1 1
2 2 2 10
3 3 3 11
4 4 4 100
5 5 5 101
6 6 6 110
7 7 7 111
8 10 8 1000
9 11 9 1001
10 12 10 1010
11 13 11 1011
12 14 12 1100
13 15 13 1101
14 16 14 1110
15 17 15 1111
16 20 10 10000

Or you use built-ins:

def make(i):
    for k in range(i+1):
        print(f"{k:>10} {bin(k):>10} {hex(k):>10} {oct(k):>10}")
        # or slice away the prefixes:
        # print(f"{k:>10} {bin(k)[2:]:>10} {hex(k)[2:]:>10} {oct(k)[2:]:>10}")


make(17)

Results in:

         0        0b0        0x0        0o0
         1        0b1        0x1        0o1
         2       0b10        0x2        0o2
         3       0b11        0x3        0o3
         4      0b100        0x4        0o4
         5      0b101        0x5        0o5
         6      0b110        0x6        0o6
         7      0b111        0x7        0o7
         8     0b1000        0x8       0o10
         9     0b1001        0x9       0o11
        10     0b1010        0xa       0o12
        11     0b1011        0xb       0o13
        12     0b1100        0xc       0o14
        13     0b1101        0xd       0o15
        14     0b1110        0xe       0o16
        15     0b1111        0xf       0o17
        16    0b10000       0x10       0o20
        17    0b10001       0x11       0o21
Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • 1
    Or you could use `'{i:>#10} {i:>#10b} {i:>#10x} {i:>#10o}'.format(i=k)` (`#` requests the prefix). – Yann Vernier Sep 21 '19 at 10:56
  • @Onur Post new question please. Your original one was answered. This is a new one about string formatting and has nothing to do with how to create octal/binary/hex numbers. Beside that - my example here included formatting with spaces. Simply calculate the width of the binary for your biggest number and format accordingly. See https://stackoverflow.com/questions/4302166/format-string-dynamically and [using-pythons-format-specification-mini-language-to-align-floats](https://stackoverflow.com/questions/9549084/using-pythons-format-specification-mini-language-to-align-floats) – Patrick Artner Sep 21 '19 at 14:22
1

The issue is you take only the mod of your number (num % base), that is to say the rightmost ("least significant") bit. What we want is not the least significant bit, but the entire decomposition.

NB: the problem here applies to all other bases too (decimal, hexadecimal...).

Indeed, if you run

n = 1000
print_formatted(n)

with your functions, you get that the decomposition of 1000 in different bases is:

1000 0 8 0

(all of them are wrong).

Here, I propose a recursive implementation:

def convert(integerToConvert, base = 2):
    '''
    When given a num and a base, will get the 
    conversion of that number in that base
    '''

    # The negative integer case is not taken into account
    if (integerToConvert < 0):
        print("ERROR: INTEGER < 0")
        return;

    # When the integer is 0, we know that we are done. There is no more bit
    if (integerToConvert == 0):
        print("WE ARE DONE")
        return;

    # get the current least significant coeff in the integerToEncode
    currentLeastSignificant = integerToConvert % base;
    print(currentLeastSignificant)

    # remove the least significant coeff and start again
    convert((integerToConvert - currentLeastSignificant) / base, base)

I ran a few quick tests:

convert(17, 2)
1
0.0
0.0
0.0
1.0
WE ARE DONE

convert(16, 2)
0
0.0
0.0
0.0
1.0
WE ARE DONE

convert(17, 16)
1
1.0
WE ARE DONE

NB1: I print numbers but you can store them in the data structure of your choice.

NB2: The most significant coefficient comes last in the printing (you can compare with your expected result)

NB3: all of these calculations are a bit expensive, so if speed matters to you, the best is actually to store all decompositions in arrays and access them (constant time).

GabCaz
  • 99
  • 1
  • 11
  • Hey man. I just saw your answer. I gave you an upvote out of appreciation. If you think that this was a well asked question, could you give me an upvote as well? – Onur-Andros Ozbek Sep 28 '19 at 17:28
  • @OnurOzbek I had already upvoted the question. I hope the answer helped, though it is probably less efficient than Patrick Artner's (I don't know what the time complexity of mode is...) – GabCaz Sep 28 '19 at 19:08