Help with hexadecimal math

Pages: 12
I'm working on a personal project where I have 2 bytes in hexadecimal code (25B0) representing animation data that rotates the roll of a 3d object. The 0 represents the biggest number that changes the rotation, the 2 is the second biggest, and the 5 and B are for extra precision. I have "25" as one element in an integer array and B0 as the next element. I think I can do math by adding B00F to 25B0 to get D5BF. However, these are two different elements in an array. Is there a way to. combine the elements to make them a single hexadecimal number?
If I understood right, you want to combine 25 and B0 to create 25B0 in hex. You can do this easily with string streams and std::hex:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <string>
#include <sstream>

int main()
{
	std::string a = "25B0";
	std::stringstream b;
	b << a;
	int c;
	b >> std::hex >> c;
	std::cout << c; //Outputs 9648 Which is 25B0 in Decimal
}


So simply add the two bytes of data into a singular string, then you can convert it from string to hex.

I'm sure there's a mathematical way to do it, but it's hex so this is quicker to come up with.
It's not clear what you mean. Maybe:

1
2
3
    int a[] = { 0x25, 0xB0 };
    
    int n = a[0] << 8 | a[1];  // or possibly a[1] << 8 | a[0] 

Union?


1
2
3
4
union rot_t {
  int parts[2];
  unsigned long entire;
};


Edit: actually more like
1
2
3
4
union rot_t {
  uint32_t parts[2];
  uint64_t whole;
};
Last edited on
Wow, three replies and three different ways. I'm having a hard time understanding any one of them. For instance, what does << and >> do?

Yes, I want to combine 0x25 and 0xB0 to make one hexadecimal 0x25B0.

@zapshe How do I convert the two integer hex numbers into a singular string?

@dutch I don't understand the third line of your code. What is it doing? Can you explain it please?
The << confuses me, why is there an 8? And what is the |?

@highwayman What is that code doing?
Last edited on
@dutch I don't understand the third line of your code. What is it doing? Can you explain it please?
The << confuses me, why is there an 8? And what is the |?


Lets consider 0x25. Its binary representation is 00100101. Each hexadecimal "digit" represents 4 bits in binary (often called a nybble, rather than two "digits" which is a "byte"). Since you have 2 "digits" in 0x25, the resulting binary representation has 8 bits. In order to have 4 "digits" or "nybbles" (0x25B0), you need 16 bits. Therefore you need to shift the number to the left by 8 to make room for the least significant byte you are going to append:

 
 0000 0000 0010 0101 --> LEFT SHIFT 8 --> 0010 0101 0000 0000


Notice how this creates 8 extra bits of padding. You can now simply either do a bitwise OR (|) operation, which will simply append the extra bits from the number 0xB0 at the end like so:

1
2
3
4
5
6
0010 0101 0000 0000 |
0000 0000 1011 0000
=
0010 0101 1011 0000

Which is to say 0x2500 | 0x00B0 = 0x25B0


Or you can simply just add them together using regular addition. Dutch's method uses the former:
1
2
auto num = 0x25 << 8 | 0xB0;
//That is, shift 0x25 by 8 bits to make room to add 0xB0 at the end. Then, OR them together. 


*Note. I put quotes around "digit" because a digit is a term that is only used to refer to a number in Base-10, which is 0 - 9.
Last edited on
what does << and >> do?

Those are input (>>) and output (<<). With string streams, you can output data to the stream to be inputted later.

http://www.cplusplus.com/reference/sstream/stringstream/stringstream/


When used with std::cout, you'll use "<<" and anything that comes after will be outputted to the console. When used with std::cin ">>", the user will be expected to input data from the console.

How do I convert the two integer hex numbers into a singular string?


You can do something like this:

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <string>

int main()
{
	int aa = 12;
	std::string as = std::to_string(aa);
	int bb = 51;
	as += std::to_string(bb);
	std::cout << as; //Outputs 1251 to the console
}


Simple create a string, and then put the two bytes of data into it.
@TheToaster,

Typo in line 4 of your math. It should be 0010, not 0100.
Typo in line 4 of your math. It should be 0010, not 0100.


Good catch. Fixed.
Whole and parts are actually using the same memory in the union.
This site’s pretty good at explaining it: http://www.cplusplus.com/doc/tutorial/other_data_types/
unions got nerfed in c++ and while they still work on every compiler I know of, with strict rules turned on they error out if you try to use 2 pieces of one.
you can do the exact same thing with ugly pointer code, though.
unsigned char data[8];
uint64_t* ui = (uint64_t*)data;
etc works just like unions used to.

<< and >> shift bits in binary, which is identical to multiply by 2 or divide by 2 however many times (just like multiply and divide by 10 gets decimal digits).

| is bitwise 'or'.


all that to say, hex is friendly enough.
to shift one hex digit you multiply by 16.
so
1A * 16 is 1A0
and 1A * 256 is 1A00
so x * 256 + y will 'combine' two hex numbers. This is identical to shift with 'or' and any of many other logical or math statements that do the same things at the bit level.

the above is easy to see, though. in base 10, if you want to combine 12 and 34, multiply by 10 twice (100, like 16*16 is 256 above) to get 1200 + 34 = 1234 same exact thing just base 16 instead of base 10. Don't let other bases intimidate you, they all work exactly like base 10 does, just with different numbers. Binary works this way too: 10 and 01 10 times 4 (2*2) is 1000 and add 1001 same as the other bases.
The Toaster wrote:
Or you can simply just add them together using regular addition.

You could also multiply by 256 instead of left-shifting by 8, so the arithmetical as opposed to bit-wise view of it is:

 
int val = a[0] * 256 + a[1];

@jonnin oof I didn’t know that :/ aw.
I learned it here, so join the club. I used to use unions to avoid the mad pointer/cast hax and was quite put out to see they took that away. You can easily make a templated class or something, but taking it away.. was just petty.
Last edited on
Is there a way to. combine the elements to make them a single hexadecimal number?
I think I can do math by adding B00F to 25B0 to get D5BF


The math can be pretty simple if you stick to actual numbers instead of how they are represented,
You can get individual values from larger value, summing them is then easy, and to combine the result together is then just a matter of reversing the procedure.

for example, following code extracts individual bits, sums them and then adds together:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>

int main()
{
	using type = unsigned short;

	// B00F + 25B0 to get D5BF
	type left = 0xB00F;
	type right = 0x25B0;

	type first = ((left & 0xF000) >> 12) + ((right & 0xF000) >> 12);
	std::cout << "first value " << std::hex << first << std::endl;

	type second = ((left & 0x0F00) >> 8) + ((right & 0x0F00) >> 8);
	std::cout << "second value " << std::hex << second << std::endl;

	type third = ((left & 0x00F0) >> 4) + ((right & 0x00F0) >> 4);
	std::cout << "third value " << std::hex << third << std::endl;

	type fourth = (left & 0x000F) + (right & 0x000F);
	std::cout << "4th value " << std::hex << fourth << std::endl;

	type total = (first << 12) + (second << 8) + (third << 4) + fourth;
	std::cout << "total " << std::hex << total << std::endl;
}


Compile:
https://rextester.com/LKFP53410
Last edited on
@jonnin agreed lol. Hm.... it would be fun to make a class like that tho I will say...
@The Toaster. Excellent visual examples to back up the concept. I learn best visually so what you're saying about << (left shift) and shifting binary bits to the left makes sense. Fascinating. So the bitwise | (OR) which I normally use as || (OR) in if statements can append the bits of 0xB0 to the bits of 0x25 - heck, we're modifying the integer on a binary level to create one single integer of four digits. Also, who would've figured that OR can add binary digits together? I see, so digit is to base-10 as bit is to binary base-2. What are digits called in base-16 hexadecimal?

@MikeyBoy Somehow that leaves me even more confused. Maybe it will make more sense if I return to that link later.

@zapshe When using strings, << (left shift) is output and >> (right shift) works as input. I guess with C++ operators have multiple meanings. Output writes to file or to console and I have only seen input used to get user input. Also, to_string() is a function that creates a string from integer? I see, so I can create a string from integers and combine the strings with the += (plus assign) operator that adds the two together.

@highwayman Nope, still confused. I have zero idea what you're talking about. Also, I don't know classes or structs so that confuses me. There might be a YouTube video that explains Unions visually. What the heck is a uint64_t?

@jonnin << and >> is the same as multiply/divide by 2? Multiply by 16 to shift one hex digit. 1A * 16 * 16 = 1A00 I understand. 1A00 + B0 = 1AB0? Then X * 256 + Y = four digit hex number. Brilliant! Holy smokes, the same thing works in base-10. 34 * 100 + 24 = 3424. Even for binary. Numbers sure are interesting.

@dutch well said.

@malibor I just realized that you can add hex digits in the same way as base-10

25B0
+B00F = D5BF

Just add the bottom digit to the corresponding top digit. Besides that the word "type" confuses me and the >> 12 which I have the assumption is a "shift right" so whatever that does.
Last edited on
So the bitwise | (OR) which I normally use as || (OR) in if statements can append the bits of 0xB0 to the bits of 0x25 - heck, we're modifying the integer on a binary level to create one single integer of four digits.


Yes. Conceptually, a bitwise OR is similar to addition, and a bitwise AND is similar to multiplication in ways. You can think of a bitwise OR (|) as the same as a logical OR (||) except it operates on individual bits (1's and 0's) instead of booleans (true's and false's).

see, so digit is to base-10 as bit is to binary base-2. What are digits called in base-16 hexadecimal?


Most people don't speak in terms of "digits" in hex. We usually talk in terms of bytes (2 hex digits together). But I have heard the term "nibble" or "nybble" used when referring to a single hex digit. There could be other terms but I don't know them.

When using strings, << (left shift) is output and >> (right shift) works as input. I guess with C++ operators have multiple meanings.


Actually, if you want to be 100% accurate, then even the usage of << and >> for streams are actually wrappers around the bit-shift operators. There is no such thing as an "output" or "input" operator in C++, so the designers of the standard library used a technique known as operator overloading to redefine a particular operator for a particular class type. This definition can be seen here:
http://www.cplusplus.com/reference/ostream/ostream/operator%3C%3C/

When you learn more about the capabilities of classes, you will probably encounter operator overloading again in the future.

Also, I don't know classes or structs so that confuses me. There might be a YouTube video that explains Unions visually. What the heck is a uint64_t?


1.) Unions are a data type that is only as large as its largest member. For example:
1
2
3
4
5
union MyUnion
{
    int a;
    char b;
}


I will assume here that the size of an int is 4 bytes and the size of a char is 1 byte (which is most common). That means, an object of type MyUnion will be at most 4 bytes. If you write to the int variable "a" inside of a MyUnion object, you actually overrwrite the b variable as well. If you only write to "b", then you overwrite the first byte of the 4 byte MyUnion type (and thus the first byte of a). In essence, a union can be used to pack types together so that only one of the types is used at a time. All of these types occupy the exact same memory location, so that means only the biggest type will determine the size of the entire union.

However, raw unions in C++ are becoming increasingly rare and you will likely not encounter them often.

2.) A uint_64 is an unsigned 64 bit integer type
https://en.cppreference.com/w/cpp/types/integer
Last edited on
Wow, you guys sure know a lot. I'm now prepared to write my program. The easiest thing I can do is use combinedVal = myArray[0] * 256 + myArray[1] to combine 0x25 and 0xB0 and then I can add B00F to get D5BF.

@TheToaster Thank you, you're very informal.
Last edited on
Pages: 12