Note that for purposes of undefinition, the result is undefined if the sign bit flips at any multiply by 2 along the way, even if the end result would have the same sign bit.
Finally, I think that if an implementation is configured such that it may set <int>::is_modulo to true, regardless if whether it actually does, such as in GCC and Clang -fwrapv, then overflow/underflow should be defined as (int)((unsigned)x << y). is_modulo's definition would be updated to include <<.
How do you define whether it changes the sign bit? Sounds to me like you're
asking for it to be "defined if it worked, undefined if it didn't", which sounds
a lot like "it's undefined". If you can't give me the rules for when it does
work reliably, what's the point?
On Monday 28 July 2014 10:12:17 Myriachan wrote:
> I'm not saying it's undefined if it *may* change the sign bit, I'm saying
> it's undefined if it *does* change the sign bit. This means that it will
> be defined unless you overflow or underflow the result. The end definition
> thus makes left shift an identical operation for signed and unsigned
> integers in one's-complement and two's complement representation so long as
> overflow/underflow does not occur; only sign-magnitude changes.
How do you define whether it changes the sign bit? Sounds to me like you're
asking for it to be "defined if it worked, undefined if it didn't", which sounds
a lot like "it's undefined". If you can't give me the rules for when it does
work reliably, what's the point?
> Note that for purposes of undefinition, the result is undefined if the sign
> bit flips at any multiply by 2 along the way, even if the end result would
> have the same sign bit.
Signed integers have UB overflow so you can't define it like that.
> Finally, I think that if an implementation is configured such that it may
> set <int>::is_modulo to true, regardless if whether it actually does, such
> as in GCC and Clang -fwrapv, then overflow/underflow should be defined as
> (int)((unsigned)x << y). is_modulo's definition would be updated to
> include <<.
In one's-complement or two's-complement representation, x << y would be defined when the high y+1 bits of x are equal.
x << y for nonnegative x is defined when x <= std::numeric_limits<decltype(+x)>::max() / pow(2, y), with this division rounded toward zero.
x << y for negative x is defined when x >= std::numeric_limits<decltype(+x)>::min() / pow(2, y), with this division rounded toward zero.
Justification for the rounding toward zero:
For 5 of 6 cases--the 6th case being negative numbers on two's-complement--the maximal value is a Mersenne number,
On Monday 28 July 2014 16:16:59 Myriachan wrote:
> In one's-complement or two's-complement representation, x << y would be
> defined when the high y+1 bits of x are equal. In sign-magnitude
> representation, x << y would be defined when the magnitude portion does not
> overflow.
What if we just leave it implementation-defined?
Shifting of unsigned integers is a well-defined operation. Shifting signed
integers is implementation-defined and may change the sign bit.
Done. It works, we just don't know what it will do.
I'm not sure you considered a sign-magnitude machine that stores the sign in
the lowest bit instead of the highest. That is:
magnitude:sign shifted
0000 001:1 0000 011:0 (-1 << 1 == 3)
or 0000 001:1 0000 011:1 (-1 << 1 == -3)
Depending on whether it shifts in a zero or a sign bit.
On one's-complement machines, shift left would probably have to be implemented as a loop of add instructions to repeatedly add a number to itself, since true shifting would have strange results.
On Tuesday, 29 July 2014 02:29:43 UTC+1, Myriachan wrote:
On one's-complement machines, shift left would probably have to be implemented as a loop of add instructions to repeatedly add a number to itself, since true shifting would have strange results.What’s wrong with negating, shifting left and negating again?
To me, the only logical way to mandate operator<<(signed whatever, unsigned whatever) would be tois convenient so long as it achieves the same result. If the Standard defined shifting left as iterative multiplication by 2, compilers would still use a shift-left immediat preserve any sign bit (e.g. on a 2’s complement machine, S-x(1)-x(2)-...-x(n) << 1U should become S-x(2)-x(3)-...-x(n)-0, etc. But to mandate a specific behaviour could/would? make the operation inefficient.