0
\$\begingroup\$
///
#include <stdio.h>
void
ConvertNumberToBinary(int Number, int *Array)
{
    for(int I = 0;
        I < 32;
        ++I)
    {
        int ZeroOrOne = Number%2;
        if(ZeroOrOne < 0)
        {
            ZeroOrOne = 1;
        }
        *(Array + I) = ZeroOrOne;
        Number = (Number >> 1);
    }
}



int main() 
{
    int Number = 5;
    int NumberInBinary[32] = {};
    ConvertNumberToBinary(Number, NumberInBinary);
    
    return(0);
}
///

A way to convert a number to binary is to use the mod operator, this thing -> % will give you the remainder of a divide for whole numbers. For example (4%3) will give you a remainder of 1. (3 goes into 4 one time, there is a remainder of 1 since 1 can't be divided by 3)

What's really useful about mod in solving this problem is it can effectively tell you the least-significant bit of any number in binary. The least significant bit representing 2^0 or 1 when turned on.

In order to do this all you have to do is divide any number by 2 and the remainder of that divide will either give you ZeroOrOne which maps directly to the least significant bit of any binary number.

I.e 000011001 
            ^ Any number Mod by 2 (Number%2) will tell you the value for this bit

So in the code that's exactly what happens, we pass in a Number to the "ConvertNumberToBinary" function and once we start looping through our array of numbers, we take the Number we passed in, we Mod it by 2, and store that remainder in the ZeroOrOne variable, the we write to the array using that variable.

A second thing we do is we take the number and shift it down by one to remove the least-significant bit so we can see the data for the next bit.

Here's the steps viewing the number 5 in binary
0101
   ^ This Number%2 will be 1, we store that in the array, then we shift
0010 --- 1 < This 1 has been shifted out
   ^ We now mod this by 2 and check its value, it is zero, we store zero into the array then shift
0001 --- 0 < This 0 has been shifted out
   ^ We mod by two again and get 1, we store that in the array, then we shift.
0000 --- 1 < This 1 has been shifted out
--- Repeat until it's been done 32 times
--- Reach the end of the loop then break out of the function and the 
array will have the result in binary 
(it will be in little endian though - least significant bit will be the first array element) 

And that's everything so far, two things to mention though, the function only works for arrays greater than 32 elements, otherwise it will break.

And the other thing is this code

if(ZeroOrOne < 0)
{
    ZeroOrOne = 1;
}

If you pass in a negative number and mod it by two there will be a remainder, but the remainder will be -1 which isn't a binary value, so I just set ZeroOrOne to 1 if it was negative

\$\endgroup\$

3 Answers 3

2
\$\begingroup\$

Don't use raw pointers like this, especially when they're not accompanied by a size. It's a recipe for disaster.

Instead of going the arithmetic route and dividing by two repeatedly you can make use of the property of the internal representation of two's complement. I find this more direct, elegant and easier to read.

Don't limit yourself to ints, we have a wonderful template system that can make choose like this more generic with ease.

Your code gives the wrong output for negative numbers, it returns a positive number. This is confusing, I recommend you either return error on a negative input, forbid negative inputs by taking only unsigned, output the two's complement or a negative sign is returned somehow.

The final code would be (prohibiting negative numbers by using unsigned in this case):

template<class T>
void dec2bin_lsb_first(unsigned T v, std::vector<char>& output){
  for(int i=0; i < std::numeric_limits<unsigned T>::digits(); i++){
    output.emplace_back(v&1);
    v >>= 1;
  }
}
\$\endgroup\$
2
  • \$\begingroup\$ "Your code gives the wrong output for negative numbers, it returns a positive number." Hmm I tested it with a debugger and it still works fine? I just noticed though that if you do a (divide by 2) instead of a (bit-shift >> by 1) to move the bits down for a negative number then It will produce the positive binary representation of that number in the array. In the original post though, I'm not dividing negative numbers to move bits down so Idk how you got positive binary numbers in the array when passing negative numbers to the function. \$\endgroup\$
    – nates7
    Nov 22, 2021 at 15:21
  • \$\begingroup\$ What I meant is, getting a positive result of a negative input to a conversion is surprising and seemingly wrong. \$\endgroup\$
    – Emily L.
    Nov 23, 2021 at 18:19
1
\$\begingroup\$

Formatting conventions

It is your program and you are free to format it any way you want to, but sticking to conventions makes it easier for others to read.

I personally prefer my function definitions and for loops on one line unless they are very long. Most people use either camelCase or snake-case with variables starting with a lower case letter.

ZeroOrOne

If you used an unsigned int for Number then you would not have to worry about it being negative and simplified it to the following:

*(Array + I) = Number % 2;

std::bitset

Since C++11 we can use std::bitset do all of this for us very neatly like the following:

#include <bitset>

int main()
{
    std::bitset<32> bs(5);
}
\$\endgroup\$
4
  • \$\begingroup\$ Thanks for the feedback, I'll keep those things in mind if I create another one. \$\endgroup\$
    – nates7
    Nov 21, 2021 at 22:32
  • \$\begingroup\$ I'm curious why you're recommending division by 2 as "showing intention", when the intention is to move to the next bit, not so much to halve the number \$\endgroup\$
    – harold
    Nov 21, 2021 at 22:32
  • \$\begingroup\$ @harold, I'm thinking of a base like in the following: TIO, but I get what you are saying. \$\endgroup\$
    – jdt
    Nov 21, 2021 at 22:44
  • \$\begingroup\$ OK, fair enough \$\endgroup\$
    – harold
    Nov 21, 2021 at 22:46
1
\$\begingroup\$

I would update Emily L's answer in the following ways:

  • return values should be returned, not using "out" parameters.
  • It is not "decimal to binary". It is an integer in internal form, which is in no way "decimal".
  • use uint8_t, or if using a char actually store '0' and '1' rather than 0 and 1.
  • prefer prefix increment.

You don't need to explain to us how formatting a number in some base works. Though, your explanation does help us know your level of understanding at this point.

To clarify the second point above: you are not "converting" a number to binary. You are formatting a number into a binary representation. Normally this is done to produce a string, not an array of integers. The native number is an abstraction, not a text string in any base. Though, you can be aware that the internal representation in the CPU is in fact binary: you are doing this with the use of >> instead of /2, though you are staying abstract with %2.

It might be instructive to have your function format a number as any base. Then you will stick to the abstraction of the native number as "an integer" not a group of bits, and it provides a rationale as to why you return an array of small numbers rather than a string: another function will assign character representation to the digits, taking care of what to do with bases > 10, or Chinese digits etc.

The function would look like this:

template <typename T>
std::vector<uint8_t> express_as_base (T number, uint8_t base)
{
    std::vector<uint8_t> result;
    if (number < 0) {
       number= -number;
       result.push_back(255);  // special flag indicates negative
    }
    while (number) {
       const auto digit= number % base;
       number /= base;
       result.push_back (digit);
    }
    return result;
}

More note on your original code:
return(0);
Don't put extra parens around your return value. return is not a function call! Besides being idiomatic (different things should look different), habitually using these extra parens can actually mean something different as of C++11. It will suppress automatically using move when returning a suitable local value, and for some kinds of templates, changes the deduced value category for the return type.

You never use anything from <stdio.h>. And if you were, you should be using <cstdio> instead. And you should really be using the C++ library functions, not the old C library.

\$\endgroup\$
1
  • \$\begingroup\$ "You never use anything from <stdio.h>." Oh lol, I thought you needed stdio.h to define main(). I never asked myself why that would be correct so out of habit I would include stdio.h whenever I write main(). Same with windows.h and WinMain(). \$\endgroup\$
    – nates7
    Nov 22, 2021 at 16:22

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.