Weary of using -1 for max unsigned values

Discussion in 'C Programming' started by Martin Wells, Oct 25, 2007.

1. Martin WellsGuest

I commonly use the likes of -1 for max unsigned values, or something
like -7 to get 6 away from the max value; sort of like:

unsigned x = ...

if (-7 == x) puts("x is 6 away from its maximum");

Since I rarely use anything smaller than int, this has never been a
problem. Obviously though, there'd be a problem if I tried:

char unsigned x = ...

if (-7 == x) puts("x is 6 away...

The reason there'd be a problem is that x could be promoted to a
SIGNED int.

So I'm wondering... what's the handiest way of checking this for any
unsigned integer expression?

In the case of unsigned char, we can do
if ((char unsigned)-7 == x)

Similarly in the case of unsigned short, we can do
if ((short unsigned)-7) == x)

But what I'm looking for is a universal method. My first thoughts were
something like

#define MAX(expr) ( (expr) * (char_unsigned)0 - (char
unsigned)1 )

The problem with this of course though is that, even though we have
the casts, everything will still get promoted to int (either signed or
unsigned depending on INT_MAX).

Anyone got any ideas such that I can write universal code such as?:

if (MAX(x)-6 == x) puts("x is 6 away from its maximum");

If we had a typeof operator it would probably be something like:

#define MAX(expr) ( (typeof expr)-1 )

I have the likes of "-1 == x" littered throughout my code... and yes
it works fine... but I'd like something more sustainable just in case
I'd choose to use something smaller than an int.

Martin

Martin Wells, Oct 25, 2007

2. Guest

Martin Wells wrote:
> I commonly use the likes of -1 for max unsigned values, or something
> like -7 to get 6 away from the max value; sort of like:
>
> unsigned x = ...
>
> if (-7 == x) puts("x is 6 away from its maximum");
>
> Since I rarely use anything smaller than int, this has never been a
> problem. Obviously though, there'd be a problem if I tried:
>
> char unsigned x = ...
>
> if (-7 == x) puts("x is 6 away...
>
> The reason there'd be a problem is that x could be promoted to a
> SIGNED int.
>
> So I'm wondering... what's the handiest way of checking this for any
> unsigned integer expression?

if (UINT_MAX-6 == x ) ...

Replace UINT_MAX with UCHAR_MAX, USHRT_MAX, ULONG_MAX, ULLONG_MAX, or
SIZE_MAX, as appropriate.

, Oct 25, 2007

3. Martin WellsGuest

james:

> if (UINT_MAX-6 == x ) ...
>
> Replace UINT_MAX with UCHAR_MAX, USHRT_MAX, ULONG_MAX, ULLONG_MAX, or
> SIZE_MAX, as appropriate

Using TYPE_MAX is no more universal than (TYPE)-1. I'm looking for
something I can use with any unsigned integer type.

Martin

Martin Wells, Oct 25, 2007
4. Guest

Martin Wells you come up with the silliest problems.
first memset, now this..

, Oct 25, 2007
5. Ian CollinsGuest

wrote:
> Martin Wells you come up with the silliest problems.
> first memset, now this..
>

Now what? Please retain enough context for your post to make sense.

--
Ian Collins.

Ian Collins, Oct 25, 2007
6. Mark McIntyreGuest

On Thu, 25 Oct 2007 13:16:41 -0700, in comp.lang.c , Martin Wells
<> wrote:

>james:
>
>> if (UINT_MAX-6 == x ) ...
>>
>> Replace UINT_MAX with UCHAR_MAX, USHRT_MAX, ULONG_MAX, ULLONG_MAX, or
>> SIZE_MAX, as appropriate

>
>
>Using TYPE_MAX is no more universal than (TYPE)-1. I'm looking for
>something I can use with any unsigned integer type.

Seems to me you want a template type.
Unfortunately C++ is thattaway ---->>

BTW I can't say I've ever encountered any actual real-world example of
your problem, so perhaps this is a solution looking for a problem...
..
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan

Mark McIntyre, Oct 25, 2007
7. Charlie GordonGuest

"Martin Wells" <> a écrit dans le message de news:
...
> james:
>
>> if (UINT_MAX-6 == x ) ...
>>
>> Replace UINT_MAX with UCHAR_MAX, USHRT_MAX, ULONG_MAX, ULLONG_MAX, or
>> SIZE_MAX, as appropriate

>
>
> Using TYPE_MAX is no more universal than (TYPE)-1. I'm looking for
> something I can use with any unsigned integer type.

Here is one:

#define MWELLS_CMP(val, x) \
(((unsigned long long)(long long)(val) & \
(-1ULL >> ((sizeof(1ULL) - sizeof(x)) * CHAR_BIT)) == (x))

Use it as:

if (MWELLS_CMP(-6, x)) ...

It should work for any x with unsigned type.
Both val and x are evaluated once in the macro expansion.
A smart compiler should generate efficient code for types smaller than
unsigned long long, but it might not.

--
Chqrlie.

Charlie Gordon, Oct 26, 2007
8. Martin WellsGuest

Chqrlie:

> #define MWELLS_CMP(val, x) \
> (((unsigned long long)(long long)(val) & \
> (-1ULL >> ((sizeof(1ULL) - sizeof(x)) * CHAR_BIT)) == (x))
>
> Use it as:
>
> if (MWELLS_CMP(-6, x)) ...

(You've got an extra open parenthesis at the very start)

I'm gonna think out loud here and see if I understand it

(

(unsigned long long)(long long)(val)

Here you take val, convert it to a long long, then to an unsigned long
long. Why don't you go straight to an unsigned long long?

&
(
-1ULL >>
( (sizeof(1ULL) - sizeof(x)) * CHAR_BIT )
)

== (x)
)

Now you take the byte difference between unsigned long long and the
expression, and multiply it by CHAR_BIT to get the bit difference. You
shift the max value of unsigned long long to the right by this bit
difference. You then compare this value to X, yielding an int value of
either 1 or 0, and you then bitwiseAND this with the previous value
above. I haven't fully thought it through but it seems you might have
an error above as regards operands and operator precedence. Anyway,
two flaws that stick out are:

1) Unsigned types can have padding, so sizeof isn't reliable for the
amount of value representational bits.
2) Use of unsigned long long is specific to C99. If we're gonna bother
with C99, then we'd be better off to go with int_max_t or whatever
it's called. As for C89, we can only go with unsigned long... but the
implemenation could provide a bigger type.

Martin

Martin Wells, Oct 26, 2007
9. Dan HenryGuest

On Thu, 25 Oct 2007 10:48:01 -0700, Martin Wells <>
wrote:

>
>I commonly use the likes of -1 for max unsigned values, ...

-1 does not convert to the maximum unsigned value for one's complement
representation.

--
Dan Henry

Dan Henry, Oct 27, 2007
10. James KuyperGuest

Dan Henry wrote:
> On Thu, 25 Oct 2007 10:48:01 -0700, Martin Wells <>
> wrote:
>
>> I commonly use the likes of -1 for max unsigned values, ...

>
> -1 does not convert to the maximum unsigned value for one's complement
> representation.

The representation of the signed integer type is irrelevant. It's the
value that gets converted, not it's representation. The relevant rule is
given in 6.3.1.3p2:

"Otherwise, if the new type is unsigned, the value is converted by
repeatedly adding or subtracting one more than the maximum value that
can be represented in the new type until the value is in the range of
the new type."

In the case of -1, that means adding "one more than the maximum value"
to -1, which gives you exactly the maximum value.

James Kuyper, Oct 27, 2007
11. CBFalconerGuest

Dan Henry wrote:
> Martin Wells <> wrote:
>
>
>> I commonly use the likes of -1 for max unsigned values, ...

>
> -1 does not convert to the maximum unsigned value for one's
> complement representation.

Yes it does. This is a portable technique.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>

--
Posted via a free Usenet account from http://www.teranews.com

CBFalconer, Oct 27, 2007
12. Charlie GordonGuest

"James Kuyper" <> a écrit dans le message de news:
wsJUi.447\$TO4.392@trnddc07...
> Dan Henry wrote:
>> On Thu, 25 Oct 2007 10:48:01 -0700, Martin Wells <>
>> wrote:
>>
>>> I commonly use the likes of -1 for max unsigned values, ...

>>
>> -1 does not convert to the maximum unsigned value for one's complement
>> representation.

>
> The representation of the signed integer type is irrelevant. It's the
> value that gets converted, not it's representation. The relevant rule is
> given in 6.3.1.3p2:
>
> "Otherwise, if the new type is unsigned, the value is converted by
> repeatedly adding or subtracting one more than the maximum value that can
> be represented in the new type until the value is in the range of the new
> type."
>
> In the case of -1, that means adding "one more than the maximum value"
> to -1, which gives you exactly the maximum value.

The likely consequence of this is that casting a int to unsigned on one of
those obsolete museum pieces emits code, possibly even a test and one or two
jumps.

One's complement is nothing to worry about.

--
Chqrlie.

Charlie Gordon, Oct 27, 2007
13. Charlie GordonGuest

"Martin Wells" <> a écrit dans le message de news:
...
> Chqrlie:
>
>> #define MWELLS_CMP(val, x) \
>> (((unsigned long long)(long long)(val) & \
>> (-1ULL >> ((sizeof(1ULL) - sizeof(x)) * CHAR_BIT)) == (x))
>>
>> Use it as:
>>
>> if (MWELLS_CMP(-6, x)) ...

>
>
> (You've got an extra open parenthesis at the very start)

Actually, there is a missing parenthesis after CHAR_BIT.

#define MWELLS_CMP(val, x) \
(((unsigned long long)(long long)(val) & \
(-1ULL >> ((sizeof(1ULL) - sizeof(x)) * CHAR_BIT))) == (x))

> I'm gonna think out loud here and see if I understand it
>
> (
> (unsigned long long)(long long)(val)
>
> Here you take val, convert it to a long long, then to an unsigned long
> long. Why don't you go straight to an unsigned long long?

no good reason, let's simplify:

#define MWELLS_CMP(val, x) \
(((unsigned long long)(val) & \
(-1ULL >> ((sizeof(1ULL) - sizeof(x)) * CHAR_BIT))) == (x))

> &
> (
> -1ULL >>
> ( (sizeof(1ULL) - sizeof(x)) * CHAR_BIT )
> )
>
> == (x)
> )
>
> Now you take the byte difference between unsigned long long and the
> expression, and multiply it by CHAR_BIT to get the bit difference. You
> shift the max value of unsigned long long to the right by this bit
> difference.

Correct: this produces the value UCHAR_MAX, UINT_MAX, ULONG_MAX or
ULLONG_MAX depending on the type of x.

> You then compare this value to X, yielding an int value of
> either 1 or 0, and you then bitwiseAND this with the previous value
> above. I haven't fully thought it through but it seems you might have
> an error above as regards operands and operator precedence.

With the missing parenthesis, the value produced in the step above is used
to mask off the high bits of val sign extended to (at least) 64 bits.

> Anyway,
> two flaws that stick out are:
>
> 1) Unsigned types can have padding, so sizeof isn't reliable for the
> amount of value representational bits.

2's complement) architectures, but the commitee forgot simple macros telling
the gory details, it is non trivial to derive INT_BIT from INT_MAX in a
macro, but can be done.

> 2) Use of unsigned long long is specific to C99. If we're gonna bother
> with C99, then we'd be better off to go with int_max_t or whatever
> it's called. As for C89, we can only go with unsigned long... but the
> implemenation could provide a bigger type.

obviously, you can tailor the macro for strict c89 and c99:

#define MWELLS_CMP89(val, x) \
(((unsigned long)(val) & \
(-1UL >> ((sizeof(1UL) - sizeof(x)) * CHAR_BIT))) == (x))

#define MWELLS_CMP99(val, x) \
(((uintmax_t)(val) & \
((uintmax_t)-1 >> ((sizeof(uintmax_t) - sizeof(x)) * CHAR_BIT))) ==
(x))

Enjoy!

--
Chqrlie.

Charlie Gordon, Oct 27, 2007
14. Dan HenryGuest

On Sat, 27 Oct 2007 16:13:16 GMT, James Kuyper
<> wrote:

>Dan Henry wrote:
>> On Thu, 25 Oct 2007 10:48:01 -0700, Martin Wells <>
>> wrote:
>>
>>> I commonly use the likes of -1 for max unsigned values, ...

>>
>> -1 does not convert to the maximum unsigned value for one's complement
>> representation.

>
>The representation of the signed integer type is irrelevant.

I was in a bit pattern frame of mind, but am feeling better now.

--
Dan Henry

Dan Henry, Oct 28, 2007