 # Math in C makes me sad.

written by Pj on Thursday August 22nd, 2019 -- 11:31 p.m.

(only moderators may edit messages)
This is just so wrong...

10 >> -3 == 0
10 >> -2 == 0
10 >> -1 == 0
10 >> 0 == 10
10 >> 1 == 5
10 >> 2 == 2
10 >> 3 == 1

Obviously 10 >> -1 should be 20, and 10 >> -2 should be 40.  Once again I'm disappointed with C's ability to do math.

The reason I came to this was that I want to do a set of things many times, but the first thing I want to do every time, the second I want to do every other time, the third I want to do every 4th time, etc.  So I figured I'd just do this:

`for (int time = 0; time < 100; time++) {  for (int thing = 0; thing < 10; thing++) {    if ((time >> (thing - 1)) & 1) continue;    // do the thing here  };};`

It still works since bit 0 is not set in the number 0, but I'm bothered that it works not because the shift right operator will shift left when given a negative number, but instead because it's just like "Ugh, IDK what a negative number means, so, whatever.  Zero." ...but that's the kind of nonsense I expect from C, which is why I figured I'd better printf() some results before assuming it works correctly.

In math, the rules are usually defined so that there's consistency in the results, as the idea is that the results of operations should be natural, not something we just made up.

Take exponents for example: 2^2 means 2*2=4, 2^3 means 2*2*2=8, 2^4 means 2*2*2*2=16, and obviously 2^1=2.  So what is 2^0? Well, mathematicians looked at the other exponents and realized that as the exponent is reduced by 1, the result is divided by 2.  So continuing with that, 2^0 = 2^1 / 2, and so 2^0 = 1.  Similarly, 2^-1 = 2^0 / 2, and so 2^-1 = 0.5.  They then looked at the curve that all of these answers create for guidance as to what stuff like 2^0.5 should be, and eventually figured out methods to calculate those results as well.

Now imagine exponents weren't already defined in mathematics, but instead a compiler author was the first person to implement them.  In that case, 2^0 would probably be 0 since it means "zero twos multiplied together", and 2^-1 would just throw an error.  If it accepted non-integers at all, it would probably implement them the way that a child might guess that they work, e.g. 3^1.5 = 3 * (half of 3) = 4.5.

...and it would then be like that forever.  No matter how many good arguments people may make for why it should work differently, the compiler author will always excuse their mistake with "it doesn't matter how it works as long as it is documented" and refuse to change it because "it'll break too many existing programs." Sometimes someone even created a standard that specifies the incorrect behavior and so they'll cite that standard as why it is correct, because following a standard is for some reason more important than doing something that makes sense.

So that's where we're at.  What the >> operator does with negative numbers doesn't make sense, and so it will never make sense, and sadly that really doesn't make it unique when it comes to math in C.