Why worry about "undefined behavior" in a >> signed type?
My question is related to this and will contain several questions.
For me, the most obvious (which means I would use it in my code) solution to the problem above is:
uint8_t x = some value; x = (int8_t)x >> 7; Yes, yes, I hear you all ... undefined behavior, and that is why I did not post my "solution".
I have a feeling (maybe it's just my sore mind) that the term "w91> behavior" is used on SO just to justify a downstream someone if the question is tagged with c / C ++.
So, let (for a while) set aside the C / C ++ standards and think about everyday life / programming, real compiler implementations and the code that they generate for modern equipment.
Considering the following:
- As far as I remember, all the hardware I came across had different instructions for arithmetic and logical shift.
- All compilers that I know translate
>>into an arithmetic shift for signed types and a logical shift for unsigned types. - I can’t remember any compiler that ever issued a
div-like low-level instruction when>>used in c / C ++ code (and we are not talking about operator overloading here). - All the equipment that I know uses U2.
So ... is there anything (any modern compiler, hardware) that behaves differently than mentioned above? Simply put, should I ever worry that the shifted value of a sign does not translate into an arithmetic shift?
My “solution” is compiled with only one low-level instruction on many platforms, while others require several low-level instructions. What would you use in your code?
True, please -)
Why worry about "undefined behavior" in a → signed type?
Because it really doesn't matter how clearly defined any particular undefined behavior is now ; The fact is that it can break at any time in the future. You rely on a side effect that can be optimized (or not optimized) at any time for any reason or without reason.
Also, I do not want to ask someone with detailed knowledge of many different compiler implementations before using something that I should not use in the first place, so I will skip it.
Yes, there are compilers that behave differently than you expect.
In particular, the optimization phases as part of compilers. They use the known possible values of the variables and get these possible values due to the lack of UB. The pointer must not be null if it was dereferenced, the integer must be non-zero, if it was used as a separator, and the value shifted to the right should be non-negative.
And this works in time:
if (x<0) { printf("This is dead code\n"); } x >> 3; In fact, is it that you are willing to take the risk?
“The standard does not guarantee yada yada” - that’s good and that’s all, but honestly, the risk is low. If you are going to run your code on some crazy platform, you usually know in advance. And if that takes you by surprise, well, then the risk you took.
Also, the workaround is terrible. If you don’t need it, it just pollutes your code base with meaningless “function calls instead of right shifts” that will be harder to maintain (and therefore bear the cost). And you can never “insert and forget” code from other places in the project - you always need to check the code for the possibility of shifting negative integers with a negative sign.