A listing of random software, tips, tweaks, hacks, and tutorials I made for Ubuntu
Category Archives: C
March 4, 2016Posted by on
I was working on a free software alternative to f.lux named openlux a while ago, and I wasn’t working on any interesting aspects of the program, just rewriting functions, which gets a bit tedious after a while, so I decided to try writing one part of the code in a slightly different manner, for fun! :D
The code is supposed to add a variable to another if a character is ‘+’, and subtract it if the character is ‘-‘. Here would be how one might implement this:
int a = /* something */; unsigned short b = /* something ... note that b is a smaller data type than A, this is important */; char chr = /* '+' or '-' */ if (chr == '+') return b + a; else return b - a;
After a few hours (maybe even a day, I’m not very good at this :P), I came up with this instead:
a = (0x80000000 | a) ^ (0x80000000 - !!(chr - 43)); if (a & 0x80000000) a++; return a + b;
To dissect this, let’s start with the easiest part (other than
return a + b;, of course :P):
!!(chr - 43).
43 is simply the value of ‘+’. Yes, okay, that was a bit cheap :P So, what
!!(chr - '+') does is that it will return 0 if chr == ‘+’, 1 otherwise. This could be have been rewritten as
(chr != '+').
Easy part out of the way, let’s look at how numbers are encoded, via example, in binary:
00000000 = 0 00000001 = 1 00000010 = 2 00000011 = 3 (...)
So far so good, right? But what about when we reach
10000000? If it’s an unsigned byte, it will return 128 (2^8). If it’s signed, however, it will return -127:
(...) 01111111 = 127 unsigned 10000000 = 128 signed 10000000 = -127 unsigned 10000001 = 129 signed 10000001 = -126 (...)
In both cases, the number will keep getting larger after
10000000, but if it’s signed, it will have wrapped around to -127.
Let’s see larger values:
(...) unsigned 11111101 = 253 signed 11111101 = -3 unsigned 11111110 = 254 signed 11111110 = -2 unsigned 11111111 = 255 signed 11111111 = -1
Notice that the largest value for the signed number is not 0, but rather, -1. This is important. If it was 0, then it would mean inverting the bits of a number would make it negative (i.e.
~n == -n).
So what would inverting the bits do?
11111111 = -1 00000000 = 0 11111110 = -2 00000001 = 1 11111101 = -3 00000010 = 2 11111100 = -4 00000011 = 3 (...)
Notice a pattern here? If we want to turn a negative number positive, we can do it via
(~n) - 1. Vice-versa, it’s
(~n) + 1.
Alright, back to the code!
(0x80000000 - (chr != '+'))
0x80000000 can be represented in binary as
10000000000000000000000000000000. In other words, the sign bit for a 32-bit integer. So if
chr == '+', it will simply evaluate as 0, and therefore, keeping
0x80000000 intact. Otherwise, it will turn it into
0x7fffffff, which is equivalent to
(0x80000000 | a) simply returns a, with the sign bit on.
Now, to deal with the xor part, let’s use a few examples to clarify:
chr = '+'; (0x80000000 | a) ^ (0x80000000 - (chr != '+')); (0x80000000 | a) ^ (0x80000000 - 0); // 0x80000000 ^ 0x80000000 cancel each other out, leaving us with 'a', unchanged (assuming a < 0x80000000) chr = '-'; (0x80000000 | a) ^ (0x80000000 - (chr != '+')); (0x80000000 | a) ^ (0x7fffffff); // assuming a < 0x80000000, this is equivalent to ~a, because the sign bit is left on, while every bit of a is inversed // as we discussed, ~a is equal to ((-a) - 1)
if (a & 0x80000000) a++; checks if the sign bit is set (i.e.
a < 0), and if so, increments
a so that it gets the correct (negative) value, for the reasons I explained earlier.
Lastly, all we have to do is
return a + b;, which should hopefully be pretty obvious :P
Let’s recap quickly
chr == '+',
a is left unchanged, and the result is simply
a + b
chr != '+',
a‘s bits are inverted, and then incremented so that it can be equivalent to
a = -a, so the result would be (assuming a’s original value, not the inverted+incremented value):
-a + b or
b - a.
I hope that you found this interesting, or at least fun to read! I’m sorry if this isn’t very clear, I’m sort of writing this to try and get to sleep, I’ll edit it tomorrow :)