Post by j***@gmail.comPost by Keith ThompsonPost by j***@gmail.comWe have this coddling of brain-damaged CPUs when a left-shift on
a negative operand is specified on sign-magnitude architectures
or something, but we have this assurance that casting a -1 to an
unsigned fills the thing with ones.
(I know calling it brain-damaged is too extreme, but ...)
I suggest you might benefit from describing these things in less
inflammatory terms.
Not sure how.
Post by Keith ThompsonThe behavor of left-shifting a negative value is undefined.
Without context, I suppose it is.
The context here is C programming unless explicitly stated otherwise,
and in that context left-shifting a negative value is undefined.
Post by j***@gmail.comBut mapping it to a logical shift left on the CPU, whatever the CPU
gives for that, seems reasonable to me.
Logical shifts work naturally on /unsigned/ types.
Post by j***@gmail.comFar more reasonable than assuming that casting a minus one to an
unsigned type should always result in a bit mask of the type's
width.
That is perfectly reasonable for modulo arithmetic on unsigned types.
There are a variety of ways one could define arithmetic and overflow for
signed and unsigned types. Different methods have their advantages and
disadvantages, and different programming languages pick different methods.
In C, unsigned arithmetic is always modulo, and so overflows wrap
around. That fits well with most hardware, and has a lot of practical
benefits. But it also means that "u < u + 1" might be false, which is
counter-intuitive for normal mathematics. You can take an unsigned
number, keep adding to it, and suddenly end up with nothing.
Signed arithmetic, on the other hand, never overflows in C. It is
undefined behaviour - as far as the language is concerned, it can't
happen. That means "x < x + 1" is /always/ true as far as C is
concerned, for signed type x. This sort of thing makes mathematical
sense, and gives compilers freedom to re-arrange and optimise code in a
way that makes sense to programmers. You may think "I don't write
conditionals like that" - but it can be surprising how often simple
mathematical manipulation of expressions are valid when overflow cannot
happen, but invalid in two's complement wraparound signed arithmetic.
(An example is "(x * 2) / 2" -> "x".)
Certain other behaviour, such as the conversion of an unsigned integer
to a signed integer when the value won't fit, are left as implementation
defined behaviour - basically, one should expect the most "natural"
behaviour on the hardware, and this should be documented by the
compiler. In many cases, this conversion will be modulo wraparound -
but compilers /may/ do something else, if that operation would be expensive.
Would it be nice to have different behaviour of C integers? Perhaps -
it is certainly a valid viewpoint. Personally, I am happy with the way
they work in most cases, but there are a few corner cases that I don't
like. I would prefer automatic promotions of unsigned char and unsigned
short to be to unsigned int, rather than signed int. I would like
certain implementation-defined behaviour, such as the conversion to a
signed integer mentioned above, to be "conditionally defined behaviour"
- if the hardware uses two's complement signed integers, then such
conversions should be defined as modulo wraparound, otherwise they can
be implementation dependent.
But whatever you think about the rules, however you think they could be
different, however you imagine they /were/ different in some old version
of C or some old compilers - the rules are there, they are clear and
consistent, and there should not be a problem learning them and using them.
Post by j***@gmail.comPost by Keith ThompsonThis is specified in N1570 6.5.7p4.
(I don't recall that I've ever felt the need to left-shift a
negative integer.)
Have you even left-shifted an unsigned char expecting specifically
for the upper bits to disappear, instead of pulling the unsigned char
into and unsigned long to do the shift and then masking the upper
bits back out with a cast back to unsigned char?
Unsigned chars promote to int (unless they are already the same size as
int, in which case they work like unsigned int). An int can always
represent all the values of the unsigned char as positive values, and
left shifting of up to 7 bits is /always/ going to work correctly.
Converting back to an unsigned char will always mask off any extra bits.
So if you have code like:
unsigned char uc = ...;
int n = ...;
uc = uc << n;
Then the code will work as expected unless "n" shifts out /all/ the bits
in the unsigned char (in which case it might work, and might not).
Post by j***@gmail.comAnd hoping the compiler (which often misses such optimizations) will
optimize out your excessive code on CPUs that will allocate a double
short register to do the long shift?
Most compilers will handle code like the above quite well.
<snip>
Post by j***@gmail.comIf I had the time, I'd go read it. I'm sure they thought they had a good reason.
I'll still push for splitting the standard if anyone is willing to discuss it.
You want to split and change the standard - but you haven't read it or
the reasoning behind it? I'd study the rationale and the latest
standard first, before trying to split it. (I don't think a rationale
for C11 has been published.)
Post by j***@gmail.comEmbedded has drastically different needs than desktop or server, and
I even think desktop and server might be well served by fine-tuning
the standard to the target.
I work with embedded programming all the time - I almost never use C on
desktops or servers (usually I use Python for such work). And the more
I learn about the subtleties of C - often from this group - the more I
realise that there is very, very little I would want to change in this
sort of area. There are a few points in the standard that would prefer
to be modified. There are a number of gcc extensions or
implementation-defined behaviours that I think are of significant
benefit to my coding - and would therefore be useful in all compilers.
There are a few (not many) points in the standard that I think could be
simplified by assuming the target is a "normal" modern processor. There
are a few changes or additions I would like in the standard library.
And there are more than a few places where the standard could benefit
from clearer language or better examples.
But the fundamentals - including the way signed and unsigned work, and
the undefined behaviours, I am happy with.
Post by j***@gmail.com-- Joel Rees
"Is the standard made for man? Or is man made for the standard?"
"Grasshopper. Go bother some other guru for a while."