svn commit: r295833 - head/sys/sys

Bruce Evans brde at optusnet.com.au
Sat Feb 20 04:42:15 UTC 2016


On Sat, 20 Feb 2016, Justin Hibbits wrote:

> Log:
>  Fix the definition of RM_MAX_END.
>
>  Even though casting from signed to unsigned is well-defined in C, it's better to
>  first cast to the larger unsigned type, then negate.

Casting ~0 is well-defined, but ~0 isn't.

The operation is complementation, not negation.

> Modified: head/sys/sys/rman.h
> ==============================================================================
> --- head/sys/sys/rman.h	Sat Feb 20 01:32:58 2016	(r295832)
> +++ head/sys/sys/rman.h	Sat Feb 20 01:34:13 2016	(r295833)
> @@ -61,7 +61,7 @@ enum	rman_type { RMAN_UNINIT = 0, RMAN_G
>  */
> #define RM_TEXTLEN	32
>
> -#define	RM_MAX_END	((rman_res_t)~0)
> +#define	RM_MAX_END	(~(rman_res_t)0)

If the operation were negation, then its result -0 would actually be well-
defined.  It would be 0 again, even in the 1's complement and signed
magnitude cases where -0 can be represented.  -0 cast to unsigned must
give 0 again, and not be all-bits-1 even if it started as all-bits 1
representing -0 in 2's complement.

~0 was under-specified in C90 as being the bitwise complement.  The value
of that is not specified unless the promotion of the orginal type is
unsigned (~(unsigned char)0 is also not specified except on exotic
machines (no longer permitted by POSIX) with unsigned char the same size
as unsigned int).

~0 is even more under-specified in C99 because C99 is more careful
with representations and has trap representations.  (This is not fixed
in C11.) Complementing a non- trap-representation might give a trap
representation if done bitwise, even starting from a nondescript value
like 0.  But if there are no trap or padding bits, the only reasonable
interpretation of the spec is that ~0 gives all-bits-1 with whatever
value that has.  In the 2's complement case, the value is -1 and casting
this to unsigned works right.  In the 1's complement case, the value
is -0 and casting this to unsigned gives unsigned plain 0.  In the
signed-magnitude case, I think it gives -0x7fffffff with 32-bit ints
and casting this to unsigned gives 0x80000001.  C90 allows other
representations, so the result can be anything.

The clearest way to write the expression is probably ((rman_res_t)-1)
like the buggy ~0 version did in the 2's complement case.

Everyone knows what -1 is.  Conversion of -1 to unsigned is better
known than what ~0 is.  It takes an explicit specification for both.

Conversion of -1 involves "[in extra precision] repeatedly adding or
subtracting one more than the maximim value that can be represented
in the new type until [the result is representable]".  Thus
((rman_res_t)-1) is maxof(rman_res_t) + 1 - 1 == maxof(rman_res_t).

(~(rman_res_t)0) involves the specification of the '~' operator
saying that complementing the bits actually works for the unsigned
case.  The precise wording is "If the promoted type [of E] is an
unsigned type, then the expression ~E is equivalent to the maximum
value representable in that type minus E".  Thus (~(rman_res_t)0)
is maxof(rman_res_t) - 0 = maxof(rman_res_t).  I think the precise
wording is redundant, but this is not clear.  Something is needed
to ensure that complementing bits doesn't give a trap representation
for the unsigned case.  Complementing only value bits wouldn't give
a trap representation, but complementing padding bits could (should)
give a trap representation.  The spec is too fuzzy to say which bits
are complemented, except in the unsigned case padding bits must not
be complemented if this would give a trap representation.

Bruce


More information about the svn-src-all mailing list