Philippe:
On Wed, May 15, 2019 at 4:33 AM Philippe Verdy <verdy_p@wanadoo.fr> wrote:
> In C, 0 is an _expression_ of type int; writing ~0 creates an unary _expression_ taking an int: the ~ operator only uses the standard type promotion (to int) by default and no other longer type (if you pass a short, that short will be promoted to an int), then it computes the bit-inversion (i.e. its a simple arithmetic addition: substract the value from the maximum of its input type and adds the minimum of that type); as this input type is an int, , it returns an int equal to (INTMAX - 0 + INTMIN). This gives the value you use in the initializer of your declared variable: It's only at that time that there will be a conversion from int to lua_Unsigned ! This conversion being possibly lossy if the target type cannot contain the full range for you initializer value (and because you did not provide an, explicit typecast to the result, the compiler should warn you.
You've got a weird concept of simple. Why go through this arithmetic
addition stuff ( which needs, among other things, a second operand ),
when the programmer requested a bitwise negation and many instruction
sets have a perfectly good unary negation instruction? ( and others
have similar logic operations which are easier for logic stuff ).
Francisco Olarte.
This is talking about the standard behavior, not about the implementation details. He's got things a little bit mixed up, which ended up making him wrong, and that's making the communication noisy, but I went and looked up the C language specification to be sure.
It's not the ~ operator that has that behavior. The ~ operator is always bitwise negation. ~0 on a 1's complement machine would be a negative zero. ~0 on a sign-magnitude machine would be INT_MIN (which is equal to -INT_MAX on those platforms instead of -INT_MAX - 1).
However, the way the spec is written, the conversion of a negative signed number into an unsigned integer is done by repeatedly adding or subtracting one more than the maximum value of the destination type until the value is in range. This means that if you start with -1 (which is out of range for uint64_t) you have to add ULONG_MAX+1 repeatedly until the value DOES fit. This is independent of the underlying bit pattern of the number, whether it's 2's complement, 1's complement, or sign-magnitude. This means that regardless of how the compiler/CPU implements it, (uint64_t)-1 MUST be equal to ULONG_MAX.
On a 2's complement machine, this is just a sign extension: copy the most significant bit into all of the new bits of the larger type. The same operation works when casting int32_t to int64_t or to uint64_t.
On a 1's complement machine or a sign-magnitude machine, casting to int64_t and uint64_t are different operations. Casting to int64_t on 1's complement is a sign extension, and on a sign-magnitude machine you have to copy the old sign bit into the new sign bit and set the old one to zero. Either way, the resulting value is -1LL, as the spec demands. Casting -1 to uint64_t is NOT just casting to int64_t and then reinterpreting the bit pattern like it is on a 2's complement machine. Because the spec demands that it must be equivalent to adding ULONG_MAX+1, you have to do a sign extension and then add 1 if you're using a 1's complement machine, and if you're using a sign-magnitude machine it's a bitwise negation followed by setting the sign bit (back) to 1.
/s/ Adam