0

I have been struggling trying to convert hexadecimal to binary. I was assigned to write a piece of code that calculates the output of a full-adder circuit. Part of the assignment is determining the base in which the numbers are being entered by the user. I have been testing whether my code was working or not and I have noticed something rather odd in hexadecimal converter.

If I enter any character that has a higher value than "F" returns the expected "Not a hexadecimal" error.

If I enter any character between "8" and "F"(both are included) I get the expected "Cannot represent with 3 binary digits" error.

I also get the expected binary values for all the numbers except "2" and "3"... I don't know what could be causing this unexpected behaviour. Any help is appreciated. You can try to run it yourself in order to understand the code better but here is my output:

0 0
1 1
2 8
3 9
4 100
5 101
6 110
7 111
8-F Cannot represent with 3 bits.
>F Not a hexadecimal.

Here is the code:

#include<stdio.h>
#include <ctype.h>
int main(){
    int optionChosen,binaryNum,reset;
    char hexadecimal;
    while (true){
        printf("Please enter input: ");
        fflush(stdin); //This line clears the input buffer 
        //Note: using fflush(stdin) is undefined and is told to be avoided whenever possible
        scanf("%c",&hexadecimal);
        hexadecimal=toupper(hexadecimal);
        if (hexadecimal>'F'){
            //Print an error message if the option chosen by the user is out of range.
            printf("Value out of range! Please enter a valid value (0-F)\n");
        }
        else{
            reset=0;//This is placed here so that the program does not ask for input all the time
            switch (hexadecimal){//using a switch to convert hexadecimal to binary
                case '0':
                    binaryNum=000;
                    break;
                case '1':
                    binaryNum=001;
                    break;
                case '2':
                    binaryNum=010;
                    break;
                case '3':
                    binaryNum=011;
                    break;
                case '4':
                    binaryNum=100;
                    break;
                case '5':
                    binaryNum=101;
                    break;
                case '6':
                    binaryNum=110;
                    break;
                case '7':
                    binaryNum=111;
                    break;
                default:
                    //return error message if default value is entered.
                    reset=1;//This makes sure the user is asked to enter input again.
                    printf("Hexadecimal %c cannot be represented with 3 bits!Please try again \n",hexadecimal);
            }
            if (reset==0){//This breaks the loop and continues with the rest of the code if the user value is in range
                printf("binaryNum = %d\n",binaryNum);
            }
        }
    }
    return 0;
}
PlagueTR
  • 78
  • 2
  • 10
  • 5
    `011` is an octal number, BTW. If you want a binary literal, use the `0b` prefix in C++14, or use bit shifting. – Arnav Borborah Mar 05 '18 at 23:09
  • 2
    I'm not sure why you say this is unexpected behavior. Not only is it true that these number can't be represented with 3 bits (even if you represent the `8` in unsigned binary, it'll still take 4 digits: `1000`), but it's also explicitly clear this is what the code is doing. – scohe001 Mar 05 '18 at 23:09
  • 1
    Numbers beginning with 0 are octal, not binary. – PaulMcKenzie Mar 05 '18 at 23:10
  • 2
    Also, `hexadecimal > 'F'` is probably not doing what you think (for example, characters `@`, `?` and space would all evaluate false) – scohe001 Mar 05 '18 at 23:11
  • 1
    FWIW, you should represent your binary number as text characters. Otherwise you are performing redundant math with internal representation. – Thomas Matthews Mar 05 '18 at 23:12
  • 4
    Umm - `fflush(stdin); //Note: using fflush(stdin) is undefined and is told to be avoided whenever possible` - with such a clear comment, why are you using `fflush(stdin)`? – Jonny Henly Mar 05 '18 at 23:13
  • 2
    Warning: Your reading materials seem to be teaching you C and not C++. Best to correct this early to avoid nasty surprises later. – user4581301 Mar 05 '18 at 23:22
  • You do get the expected values for 2 and 3. You don't get them for 4,5,6,7, because you cease using octal notation. I don't know why you think a hex number should be representable in 3 bits. You need four. Unclear what you're asking. – user207421 Mar 06 '18 at 00:15
  • Thank you. It was because we weren't yet taught of any other way of getting character input yet. I have noticed that I was accidentaly saving them as octal numbers, I just need to remove the 0 if I want to fix it. – PlagueTR Mar 06 '18 at 08:16
  • I have already fixed the issue. You are right, we are being taught C, not C++. However, this assignment was given so we could see the differences/similarities between C and C++. I am also not allowed to use anything other than: while loops, switches, if-else statements (toupper being the only exception). – PlagueTR Mar 06 '18 at 11:42
  • You may want to use `std::bitset` to hold your binary values. https://stackoverflow.com/a/88934/14065 or `std::cout << std::bitset<3>(hexadecimal)` – Martin York May 09 '18 at 00:14

3 Answers3

2

In C++, a numeric constant starting with 0 is an octal number. The constants 000 and 001 are the same in bases 2, 8 or 10. In base 8, 010 is 8 and 011 is 9. As Amav Borborah mentions in the comments, C++ now has a 0b prefix for binary literals, and 0b10 would be 2, although in this instance, it would just be a complicated way of returning your input value.

This octal-number pitfall is a legacy from C, which copied it from the B programming language. The historical reason for this is that, back in the ’70s when those languages were created, the DEC PDP-7 and a few other minicomputers had 18-bit words, so specifying a bit pattern in 3-bit chunks would work, but four hex digits would be too few and five would be too many. Back then, the language designers didn’t worry about being cryptic. The other major holdovers of it are the legacy three-octal-digit character escapes like '\123', and UNIX file permissions, which are organized into three-bit chunks and displayed as octal. This is mostly a historical curiosity today, because nobody has made computers with a word size divisible by 3 in four decades.

This is probably not how you actually want to handle the conversion. But a quick fix to store in the same format as the other values is just to remove the leading 0.

Davislor
  • 14,674
  • 2
  • 34
  • 49
  • Thank you. We have not yet learnt about the correct way of doing this. That's why I was struggling. Do you think whether I could convert it to a decimal value and then convert it to binary using the division method?(This would solve the problem but then this could also need more processing time, right(?)) – PlagueTR Mar 06 '18 at 08:13
  • Not a complete answer, because this sounds like you want to solve the problem yourself, but a simple approach might be to make a lookup table `{"00000000","00000001",...}` and use your byte value as an index. More complicated: `for ( uint_fast8_t i = 0b10000000; i != 0; i = i >> 1 )` – Davislor Mar 06 '18 at 08:40
  • Within the loop, you would print `(b & i) ? '1' : '0'`. You could optionally skip leading zeroes. – Davislor Mar 06 '18 at 08:47
  • I have already fixed the issue. The problem is I am not allowed to use tables or any lists-arrays since we were not taught about these things in class (for now). I am also not allowed to use anything other than: while loops, switches, if-else statements (toupper being the only exception). Thank you for your help. – PlagueTR Mar 06 '18 at 11:42
  • One way you could do it is with a `while` loop that counts down from bit 7 (or 31, or 63) to bit 0, testing the input against each singleton bit, and printing `0` or `1` as appropriate. You can generate the singleton bitfields with a bit-shift operation. A second `while` loop in front of the one that prints could ignore leading zeroes. – Davislor Mar 06 '18 at 20:15
0

Placing a 0 at the beginning of an integer causes it to be an octal. That is, the value you get will be in powers of 8 instead of 10.

That is why:

001 = (0*8^2 + 0*8^1 + 1*8^0) = 1

whereas:

011 = (0*8^2 + 1*8^1 + 1*8^0) = 9
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
PlagueTR
  • 78
  • 2
  • 10
0

As others have mention the problem is that literal numbers begin with a zero are in base 8.

 int x = 010; // base 8 => 8 in decimal
 int y = 011; // base 8 => 9 in decimal

Your other numbers you are faking the binary.
I would change how you print the values.

 int value = getValueToPrint();
 std::cout << std::setw(1) << std::hex << value << " "      // prints value as hex
           << std::setw(2) << std::dec << value << " "      // prints value as dec
           << std::setw(3) << std::bitset<3>(value) << "\n";// prints value in bin
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Thank you. We did not know about binary data back then, we were asked to fake binary using integers. That's why I have not changed it. – PlagueTR May 09 '18 at 00:24