cancel
Showing results for 
Search instead for 
Did you mean: 

Shifting left – a general C question

EThom.3
Senior

Hi,

I have a piece of CRC calculating code that I nicked from somewhere a couple of decades ago. I've been wondering about a few lines:

  *CRCVal ^= (*CRCVal << 8) << 4;
  *CRCVal ^= ((*CRCVal & 0xFF) << 4) << 1;

(CRCVal is a pointer to a uint16_t variable.)

What I'm curious about is the slightly weird left-shifting notation. Shouldn't it give exactly the same result if it was written like this?

  *CRCVal ^= (*CRCVal << 12);
  *CRCVal ^= (*CRCVal & 0xFF) << 5;

Does anyone have a hunch why the shifts are split up like this? Could it be something related to a specific compiler or device that handles half-byte shifts in a certain way?

By the way, it would be great if this forum had a "General programming" board. I prefer to ask questions here, as I think that the community has a nice vibe.

12 REPLIES 12

@EThom.3 wrote:

code that I nicked from somewhere .


Well, nicking random code is always a bit risky ...

 


@EThom.3 wrote:

a couple of decades ago


Especially old random code!

 


@EThom.3 wrote:

Could it be something related to a specific compiler or device that handles half-byte shifts in a certain way?.


That would be my guess.

Impossible to know without actually asking the original author.

Even then it may have been misguided - compilers in the 2000s were already pretty good at optimising such things.

 


@EThom.3 wrote:

it would be great if this forum had a "General programming" board. .


I've moved it to the 'Others: hardware and software' section.

Thanks Andrew.

While I do agree that nicking random code is risky, it was from a colleague I had at the time. I don't know the origin of it, but it has been used extensively during those decades, and works nicely. No problems at all.

I think I'll try both ways, and see if I can spot any difference in the compiled code.

Have a nice day!

I just tried replacing the original "double shift" syntax with the simpler solution. It made no difference at all to the compiled code. Not the slightest.

It may very well be that the code is so old, that the odd syntax did make a difference back then. But it clearly doesn't anymore. Nice to know.

You do still see people holding on to old lore like this - despite the fact that it's been long outmoded.

Even 2 decades ago - that's 2005 - I very much doubt that it made any difference at all.

Far better to just write the code clearly, and let the compiler get on with the arcane optimisations.

And, if you ever do come across some obscure case where this kind of micro-management really is needed, be sure to clearly comment it!

See also shifting vs multiplying or dividing by powers-of-two ...

As mentioned, I don't know the origin of the code, and it could easily be much older.

But, definitely, obscure micro-management should be commented. Or, preferably, avoided.

TDK
Guru

> Does anyone have a hunch why the shifts are split up like this? Could it be something related to a specific compiler or device that handles half-byte shifts in a certain way?

Probably for readability. A shift by 8 and then 4 indicates a full byte shift and a further 4 bits. Whereas 12 might take longer to parse.

In any case, they're functionally identical and the compiler can probably optimize it into a single instruction in release configuration.

If you feel a post has answered your question, please click "Accept as Solution".

It would be interesting to know the context. The polynomial, the use case, and the MCU it's was originally developed for.

Can be a close stab if the true original is unknown.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

The complete (now modified) function looks like this:

void CalcCRC(uint8_t Data, uint16_t *CRCVal) // XModem CRC (CCITT)
{
  *CRCVal = (uint8_t)(*CRCVal >> 8) | (*CRCVal << 8);
  *CRCVal ^= (uint8_t)Data;
  *CRCVal ^= (uint8_t)(*CRCVal & 0xFF) >> 4;
  *CRCVal ^= *CRCVal << 12;
  *CRCVal ^= (*CRCVal & 0xFF) << 5;
}

Polynomial: 0x1021

Initial value: 0

No reflection.

I don't know exactly what MCU it was made for, but most likely an 8-bit device. Possibly a PIC.

Or perhaps an 8051, if that even had a C compiler 20-30 years ago. Don't know. The only time I touched an 8051 was at the university, where it was programmed in assembly language. And I have no desire to ever use one again.