Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

condition true or false? -> (-1 < sizeof("test"))

121 views
Skip to first unread message

John Reye

unread,
May 17, 2012, 4:40:08 AM5/17/12
to
Hello,

please can someone explain why
(-1 < sizeof("test"))
is false.

What do you think the following will print?

/***********************/
#include <stdio.h>

int main(void)
{
int i;

if (-1 < sizeof("test1")) {
printf("Cool line 1\n");
}

if (-1 < -1 + sizeof("test2")) {
printf("Cool line 2\n");
}

for (i = -1; i >= -3; i--) {
if (i < i + 1U) {
printf("Here %d\n", i);
}
}
return 0;
}




I am truly shocked and amazed.

Is there any coding suggestion that will (in future) save me half an
hour of sprinkling printf's like wild and thinking my compiler is
buggy, and that logic has just died?!!!!!!

What do I need to know to avoid these surprises and is there some
"coding style" that can guard against it.

Thanks!




For the real-world code, that caused my confusion:
#include <stdio.h>
#include <limits.h>

#if EOF != -1
#error EOF is not -1
#endif

int arr[UCHAR_MAX+2]; // storage for every unsigned char, and one
additional value

#define NUM_ARRAY_ELEMENTS(arr) (sizeof(arr)/sizeof(arr[0]))

int main(void)
{
int i;
for (i = EOF; i < EOF + NUM_ARRAY_ELEMENTS(arr); i++) {
printf("%d\n", i);
arr[i+1] = i;
}
return 0;
}

John Reye

unread,
May 17, 2012, 4:55:56 AM5/17/12
to
For some more (similar) surprises:

#include <stdio.h>

int main(void)
{
int i;
char arr[1000];

if (-5 < sizeof(arr))
printf("Will this print?\n");

if (-99999999999999999 < sizeof(arr))
printf("What about this?\n");

return 0;
}

Robert Wessel

unread,
May 17, 2012, 5:38:13 AM5/17/12
to
Somewhat simplified*, C's "usual arithmetic conversions" convert
signed integers to unsigned when paired with an unsigned value for
some operator.

sizeof returns a size_t, which is unsigned, and -1 is a (signed) int.

so (-1 < sizeof("test")) is actually comparing (UINT_MAX <
sizeof("test"))

As to coding techniques... Don't compare signed and unsigned numbers
unless you really mean it. Your compiler should flag that sort of
thing at higher warning levels. MSVC will flag that sort of thing at
-W2 and above (and there a way to turn on that specific warning even
at -W1). In any event, you should really be using -W3 (assuming MSVC)
as a minimum anyway (-W4 can be somewhat painful). For GCC, turn on
"-Wconversion", which, I think, gets turn on with "-Wall".


*an except exists when all possible values of the unsigned type can be
represented in the signed type, but it doesn't apply here

BartC

unread,
May 17, 2012, 6:16:06 AM5/17/12
to
"Robert Wessel" <robert...@yahoo.com> wrote in message
news:sig9r7llsjed5lpvv...@4ax.com...
> On Thu, 17 May 2012 01:40:08 -0700 (PDT), John Reye
> <jono...@googlemail.com> wrote:

>>please can someone explain why
>> (-1 < sizeof("test"))
>>is false.

> Somewhat simplified*, C's "usual arithmetic conversions" convert
> signed integers to unsigned when paired with an unsigned value for
> some operator.

> As to coding techniques... Don't compare signed and unsigned numbers
> unless you really mean it. Your compiler should flag that sort of
> thing at higher warning levels.

I always thought C was really a multitude of languages, not one, when the
myriad compiler options are taken into accounts.

Having one module allow mixed arithmetic, and not another, is a good
example. (Someone else uses a different compiler and different switches, and
different things will happen.)

MSVC will flag that sort of thing at
> -W2 and above (and there a way to turn on that specific warning even
> at -W1). In any event, you should really be using -W3 (assuming MSVC)
> as a minimum anyway (-W4 can be somewhat painful). For GCC, turn on
> "-Wconversion", which, I think, gets turn on with "-Wall".

-Wall doesn't seem to turn on -Wconversion.

However, -Wconversion doesn't give a warning in this example:

unsigned int a=4;
signed int b=-2;

printf("%u<%d = %d\n\n", a, b, a<b);
printf("%d<%d = %d\n\n", 4, b, 4<b);
printf("%u<%d = %d\n\n", a, -2, a<-2);
printf("%d<%d = %d\n\n", 4, -2, 4<-2);

which gives conflicting results. (Presumably an integer literal such as "4"
is assumed to be signed? Having it as unsigned would be more intuitive, as
it would be impossible for it to be negative.)

--
Bartc

Ben Bacarisse

unread,
May 17, 2012, 7:28:26 AM5/17/12
to
"BartC" <b...@freeuk.com> writes:

> "Robert Wessel" <robert...@yahoo.com> wrote in message
> news:sig9r7llsjed5lpvv...@4ax.com...
>> On Thu, 17 May 2012 01:40:08 -0700 (PDT), John Reye
>> <jono...@googlemail.com> wrote:
>
>>>please can someone explain why
>>> (-1 < sizeof("test"))
>>>is false.
>
>> Somewhat simplified*, C's "usual arithmetic conversions" convert
>> signed integers to unsigned when paired with an unsigned value for
>> some operator.
>
>> As to coding techniques... Don't compare signed and unsigned numbers
>> unless you really mean it. Your compiler should flag that sort of
>> thing at higher warning levels.
>
> I always thought C was really a multitude of languages, not one, when the
> myriad compiler options are taken into accounts.
>
> Having one module allow mixed arithmetic, and not another, is a good
> example. (Someone else uses a different compiler and different switches, and
> different things will happen.)

That's an odd way to look at it. Mixed arithmetic is always allowed. I
don't regard the presence of more or fewer warnings as indicating that I
am using a different language!

<snip>
> (Presumably an integer literal such as "4"
> is assumed to be signed? Having it as unsigned would be more intuitive, as
> it would be impossible for it to be negative.)

But that would turn x < 4 into an unsigned comparison. Worse, -1 would
always be unsigned (and large). You'd need to alter a lot of details if
you change something as fundamental as the type of a literal constant.

--
Ben.

gwowen

unread,
May 17, 2012, 7:29:55 AM5/17/12
to
On May 17, 9:40 am, John Reye <jonona...@googlemail.com> wrote:

> I am truly shocked and amazed.

Utterly horrible isn't it? A bizarre misfeature.

> Is there any coding suggestion that will (in future) save me half an
> hour of sprinkling printf's like wild and thinking my compiler is
> buggy, and that logic has just died?!!!!!!
>
> What do I need to know to avoid these surprises and is there some
> "coding style" that can guard against it.

All I can suggest is to turn the compiler warnings on to the
absolutely highest possible, and understand why they warn. Compiled
with

gcc -Wall -Wextra -Werror

for example, the above code will fail to compile and the diagnostic
will be something like:

"Error: comparison between signed and unsigned types"

It'll also flag things like
if(x = 1){
...
}

where you (probably) meant

if(x==1){
...
}

and tell you what to do if you *meant* if(x=1)...

Andreas Perstinger

unread,
May 17, 2012, 7:32:42 AM5/17/12
to
On 2012-05-17 12:16, BartC wrote:
> However, -Wconversion doesn't give a warning in this example:

You need -Wsign-compare (or -Wextra) with gcc in your example.

> unsigned int a=4;
> signed int b=-2;
>
> printf("%u<%d = %d\n\n", a, b, a<b);
> printf("%d<%d = %d\n\n", 4, b, 4<b);
> printf("%u<%d = %d\n\n", a, -2, a<-2);
> printf("%d<%d = %d\n\n", 4, -2, 4<-2);
>
> which gives conflicting results.

Why are the results conflicting?

1) a < b: you are comparing unsigned int and signed int -> signed int
gets converted to unsigned -> 4 is smaller than -2 + UMAX_INT + 1

2) 4 < b: your are comparing a decimal integer constant (which is
normally of type signed int) with signed int -> no implicit conversion
-> 4 isn't smaller than -2

3) a < -2: you are comparing unsigned int with a decimal integer
constant (type signed int) -> -2 is converted to unsigned int -> 4 is
smaller than -2 + UMAX_INT + 1

4) 4 < -2: you are comparing a decimal integer constant with another
decimal integer constant -> no implicit conversion -> 4 isn't smaller
than -2

I think your problem is that in example 1 and 3 you print the signed
values (b and -2) with the %d format specifier but to see what's going
on you have to use %u because they get converted to unsigned int before
the comparison is evaluated:

printf("%u<%u = %d\n\n", a, b, a<b);
printf("%d<%d = %d\n\n", 4, b, 4<b);
printf("%u<%u = %d\n\n", a, -2, a<-2);
printf("%d<%d = %d\n\n", 4, -2, 4<-2);

Bye, Andreas

Eric Sosman

unread,
May 17, 2012, 7:38:33 AM5/17/12
to
On 5/17/2012 4:40 AM, John Reye wrote:
> Hello,
>
> please can someone explain why
> (-1< sizeof("test"))
> is false.

Because of the "usual arithmetic conversions," 6.3.1.8.
Most arithmetic operators require operands of the same type,
so for differing types the UAC's operate to reconcile them
before the operation is performed. In this case you have an
int and a size_t, and the UAC's convert the int to size_t:

if ( (size_t)-1 < sizeof("test") )

Since size_t is an unsigned type, (size_t)-1 is the largest
value the type can represent, and is a good deal greater
than (size_t)5.

Aside: I suppose that on a perverse implementation the
outcome might be different. If (size_t)-1 is mathematically
no greater than INT_MAX the conversion would go the other way.
The size_t would convert to an int before the comparison and
you'd have:

if ( -1 < (int)sizeof("test") )

I've never heard of an implementation where size_t is so
narrow, and I'm not 100% sure it would be conforming -- but
I'm not 100% sure it would be forbidden, either.

> Is there any coding suggestion that will (in future) save me half an
> hour of sprinkling printf's like wild and thinking my compiler is
> buggy, and that logic has just died?!!!!!!
>
> What do I need to know to avoid these surprises and is there some
> "coding style" that can guard against it.

Try cranking up the warning levels on your compiler. If
you use gcc, "-W -Wall" will produce warnings for comparisons
between signed and unsigned types (there's surely a more specific
"-Wsomething" for just that particular warning, but I can't be
bothered to go ferret out just what it is).

--
Eric Sosman
eso...@ieee-dot-org.invalid

BartC

unread,
May 17, 2012, 8:00:12 AM5/17/12
to


"Andreas Perstinger" <andip...@gmail.com> wrote in message
news:jp2nkq$36f$1...@dont-email.me...
> On 2012-05-17 12:16, BartC wrote:
> > However, -Wconversion doesn't give a warning in this example:
>
> You need -Wsign-compare (or -Wextra) with gcc in your example.
>
> > unsigned int a=4;
> > signed int b=-2;
> >
> > printf("%u<%d = %d\n\n", a, b, a<b);

> > which gives conflicting results.
>
> Why are the results conflicting?
>
> 1) a < b: you are comparing unsigned int and signed int -> signed int
> gets converted to unsigned -> 4 is smaller than -2 + UMAX_INT + 1

> I think your problem is that in example 1 and 3 you print the signed
> values (b and -2) with the %d format specifier but to see what's going

Because they are signed!

> on you have to use %u because they get converted to unsigned int before
> the comparison is evaluated:
>
> printf("%u<%u = %d\n\n", a, b, a<b);

This is the point. You're just demonstrating here *how* it manages to print
the wrong result!

But the fact is that b *is* signed, and needs to be displayed with %d. So in
my original example, the 4 and -2 were printed correctly in each case.

Also my gcc didn't give any warnings despite using -Wconversion.

--
Bartc

BartC

unread,
May 17, 2012, 8:05:42 AM5/17/12
to
"Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
news:0.893910e519ad620c6915.2012...@bsb.me.uk...
> "BartC" <b...@freeuk.com> writes:

>> (Presumably an integer literal such as "4"
>> is assumed to be signed? Having it as unsigned would be more intuitive,
>> as
>> it would be impossible for it to be negative.)
>
> But that would turn x < 4 into an unsigned comparison. Worse, -1 would
> always be unsigned (and large).

1 would be unsigned.

-1, assuming constant folding by the compiler, would be equivalent to a
signed integer literal of "-1".

(If not, then it remains the negation of unsigned 1, performed at runtime.
For this purpose, negating an unsigned value would need to be allowed, and I
can't see a problem with that, except the usual overflow issues).

--
Bartc



Eric Sosman

unread,
May 17, 2012, 8:10:40 AM5/17/12
to
On 5/17/2012 6:16 AM, BartC wrote:
>
> I always thought C was really a multitude of languages, not one, when the
> myriad compiler options are taken into accounts.

Not a multitude of languages, but a multitude of implementations.
Changing the compiler options is equivalent to changing the compiler.
The Standard is silent on whether different implementations must
interoperate smoothly, but the compiler's documentation should tell
you which sets of flags are and are not compatible. (For example,
it might be impossible to mix "-ILP32" and "-LP64" modules in the
same executable, even if "the same" compiler generates both.)

> which gives conflicting results. (Presumably an integer literal such as "4"
> is assumed to be signed? Having it as unsigned would be more intuitive, as
> it would be impossible for it to be negative.)

Well, "4" is not an integer literal, but that's a markup issue :)

The types of integer constants depend on their magnitude and
on the notation used, as described in 6.4.4.1p5. The constant 4
has type int, and is signed even though the value is positive.
If you think it would be "more intuitive" for the 4 to be unsigned,
please ponder `-8 / 4' and `-8 / 4u' and explain why having them
be identical would be unsurprising.

--
Eric Sosman
eso...@ieee-dot-org.invalid

BartC

unread,
May 17, 2012, 8:31:06 AM5/17/12
to
"Eric Sosman" <eso...@ieee-dot-org.invalid> wrote in message
news:jp2ps3$gnm$1...@dont-email.me...
> On 5/17/2012 6:16 AM, BartC wrote:

>> which gives conflicting results. (Presumably an integer literal such as
>> "4"
>> is assumed to be signed? Having it as unsigned would be more intuitive,
>> as
>> it would be impossible for it to be negative.)

> The types of integer constants depend on their magnitude and
> on the notation used, as described in 6.4.4.1p5. The constant 4
> has type int, and is signed even though the value is positive.
> If you think it would be "more intuitive" for the 4 to be unsigned,
> please ponder `-8 / 4' and `-8 / 4u' and explain why having them
> be identical would be unsurprising.

If you're going to make integer literals be unsigned by default, then you
would probably also change mixed arithmetic to be signed and not unsigned.

--
Bartc

Andreas Perstinger

unread,
May 17, 2012, 8:31:22 AM5/17/12
to
On 2012-05-17 14:00, BartC wrote:
> "Andreas Perstinger"<andip...@gmail.com> wrote in message
> news:jp2nkq$36f$1...@dont-email.me...
>> I think your problem is that in example 1 and 3 you print the signed
>> values (b and -2) with the %d format specifier but to see what's going
>
> Because they are signed!
>
>> on you have to use %u because they get converted to unsigned int before
>> the comparison is evaluated:
>>
>> printf("%u<%u = %d\n\n", a, b, a<b);
>
> This is the point. You're just demonstrating here *how* it manages to print
> the wrong result!
>
> But the fact is that b *is* signed, and needs to be displayed with %d. So in
> my original example, the 4 and -2 were printed correctly in each case.

I thought you were interested in the behaviour of the comparison,
weren't you? At least that was the problem of the OP.

Of course b as such is signed but in regard to the comparison with an
unsigned value it gets converted to unsigned int. Therefore I think
printing b as int just adds to the confusion.

> Also my gcc didn't give any warnings despite using -Wconversion.

$ cat test.c
#include <stdio.h>

int main(void)
{
unsigned int a=4;
signed int b=-2;

printf("%u<%d = %d\n\n", a, b, a<b);
printf("%d<%d = %d\n\n", 4, b, 4<b);
printf("%u<%d = %d\n\n", a, -2, a<-2);
printf("%d<%d = %d\n\n", 4, -2, 4<-2);

return 0;
}
$ gcc -o test -Wconversion test.c
$ gcc -o test -Wextra test.c
test.c: In function ‘main’:
test.c:8:37: warning: comparison between signed and unsigned integer
expressions
test.c:10:38: warning: comparison between signed and unsigned integer
expressions
$ gcc --version
gcc (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2

Bye, Andreas

Message has been deleted

BartC

unread,
May 17, 2012, 8:52:53 AM5/17/12
to
"Andreas Perstinger" <andip...@gmail.com> wrote in message
news:jp2r2q$nh2$2...@dont-email.me...
> On 2012-05-17 14:00, BartC wrote:

>> But the fact is that b *is* signed, and needs to be displayed with %d. So
>> in
>> my original example, the 4 and -2 were printed correctly in each case.

> Of course b as such is signed but in regard to the comparison with an
> unsigned value it gets converted to unsigned int. Therefore I think
> printing b as int just adds to the confusion.

Printing signed b as "%u" would probably itself have induced a comment, and
generated confusion of it's own.

>> Also my gcc didn't give any warnings despite using -Wconversion.

> $ gcc -o test -Wconversion test.c
> $ gcc -o test -Wextra test.c

OK, so there are even more warning levels beyond -Wall and -Wconversion.

>
> I thought you were interested in the behaviour of the comparison, weren't
> you? At least that was the problem of the OP.
>

(Regarding the OP's problem, the suggestion I made elsewhere, that mixed
arithmetic should be signed not unsigned, would have fixed it!

Well, except in the unlikely scenario that sizeof("test") was bigger than
2GB or so.)

--
Bartc

Tim Prince

unread,
May 17, 2012, 9:45:08 AM5/17/12
to
On 5/17/2012 4:38 AM, Eric Sosman wrote:
> On 5/17/2012 4:40 AM, John Reye wrote:
>> Hello,
>>
>> please can someone explain why
>> (-1< sizeof("test"))
>> is false.
>
> Because of the "usual arithmetic conversions," 6.3.1.8.
> Most arithmetic operators require operands of the same type,
> so for differing types the UAC's operate to reconcile them
> before the operation is performed. In this case you have an
> int and a size_t, and the UAC's convert the int to size_t:
>
> if ( (size_t)-1 < sizeof("test") )
>
> Since size_t is an unsigned type, (size_t)-1 is the largest
> value the type can represent, and is a good deal greater
> than (size_t)5.
>
> Aside: I suppose that on a perverse implementation the
> outcome might be different. If (size_t)-1 is mathematically
> no greater than INT_MAX the conversion would go the other way.
> The size_t would convert to an int before the comparison and
> you'd have:
>
> if ( -1 < (int)sizeof("test") )
>
> I've never heard of an implementation where size_t is so
> narrow, and I'm not 100% sure it would be conforming -- but
> I'm not 100% sure it would be forbidden, either.
>

I've tried to work with an organization whose coding policies were
incompatible with size_t taking a wider data type than int. The
assignment probably was punishment for my background in Fortran, which
may still account for not fully understanding the possibilities you
describe.


--
Tim Prince

James Kuyper

unread,
May 17, 2012, 9:58:34 AM5/17/12
to
On 05/17/2012 05:38 AM, Robert Wessel wrote:
...
> Somewhat simplified*, C's "usual arithmetic conversions" convert
> signed integers to unsigned when paired with an unsigned value for
> some operator.
...
> *an except exists when all possible values of the unsigned type can be
> represented in the signed type, but it doesn't apply here

The first part of the usual arithmetic conversions is to apply the
integer promotions, which means that if int can represent all values of
the unsigned type, it will be promoted to int (6.3.1.1p2). Your
assertion to the contrary notwithstanding, that can apply here: SIZE_MAX
< INT_MAX is possible, though rather unusual.

I believe that the exception you're referring to is the one that applies
after the integer promotions, and only if they don't convert the
unsigned value to 'int'. It applies only if the integer conversion rank
of the unsigned type is lower than the rank of the signed type. For
instance, if UINT_MAX < LLONG_MAX (which is pretty likely to be true,
but not a requirement), the expression -1LL < 5U will be evaluated using
long long, and will therefore be true.
--
James Kuyper

James Kuyper

unread,
May 17, 2012, 10:04:27 AM5/17/12
to
On 05/17/2012 07:38 AM, Eric Sosman wrote:
...
> outcome might be different. If (size_t)-1 is mathematically
> no greater than INT_MAX the conversion would go the other way.
> The size_t would convert to an int before the comparison and
> you'd have:
>
> if ( -1 < (int)sizeof("test") )
>
> I've never heard of an implementation where size_t is so
> narrow, and I'm not 100% sure it would be conforming -- but
> I'm not 100% sure it would be forbidden, either.

Can you give a justification for your doubts about whether such an
implementation could be conforming?

The lower limit for SIZE_MAX is 65535, and there's no upper limit for
INT_MAX, so I don't see why an implementation where SIZE_MAX < INT_MAX
could not be fully conforming.
--
James Kuyper

Ben Bacarisse

unread,
May 17, 2012, 10:32:25 AM5/17/12
to
This discussion is confusing because it is not clear what changes you
are thinking of. Are you proposing that the operand of - not be subject
to integer promotion, or are you proposing to change how integer
promotion is defined?

What about x < 4 being an unsigned compression? Are proposing a change
to the definition of <, to the usual arithmetic conversions, or something
else?

If all you are saying that there is probably a language a bit like C in
which integer constants are all unsigned, and that such a language might
have fewer surprises for people learning it, then I am happy to agree.

--
Ben.

BartC

unread,
May 17, 2012, 12:02:03 PM5/17/12
to
"Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
news:0.acebabc8187a84f5ab75.2012...@bsb.me.uk...
> "BartC" <b...@freeuk.com> writes:
>> "Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message

> This discussion is confusing because it is not clear what changes you
> are thinking of. Are you proposing that the operand of - not be subject
> to integer promotion, or are you proposing to change how integer
> promotion is defined?

I hadn't planned to propose any changes! Just clarifying the signedness of
4, to make sense of my examples where the < was giving conflicting results
even though values being compared were apparently the same, yet there was no
warning given. (As it turns out, gcc needs -Wextra to give the warning.)

> What about x < 4 being an unsigned compression? Are proposing a change
> to the definition of <, to the usual arithmetic conversions, or something
> else?

If literals are unsigned, then yes, it would cause problems *because* of C
using unsigned modes for mixed arithmetic. So it would be a massive change
compared to, say, getting rid of trigraphs, which hardly anyone would
notice.

> If all you are saying that there is probably a language a bit like C in
> which integer constants are all unsigned, and that such a language might
> have fewer surprises for people learning it, then I am happy to agree.

(Actually I am working on such a language. There, I found things fell into
place better if literals were unsigned. Also it will use signed arithmetic
where operands are mixed, having briefly considered doing what C does.

That doesn't remove all problems, but I felt it was generally more useful.
And would have given the expected result in "-1<sizeof("test")", whether
size_t was signed or not.)

--
Bartc

Robert Wessel

unread,
May 17, 2012, 12:39:30 PM5/17/12
to
Do you mean the warning on the conversion implied by the format
string? I'm not sure what warning level you need to get GCC to
complain about that one (or if it will at all), but that's a much more
difficult problem - remember that the format string is interpreted at
runtime.

Note that some compilers (and some lints) do attempt a (partial)
analysis if the format string is known at compile time, and you're
calling a library function the compiler specifically knows to be using
that type of format string (again, I don't know if GCC will).

This is basically the same problem you have passing any mismatching
parameters and format string to a printf-like function.

James Kuyper

unread,
May 17, 2012, 1:02:49 PM5/17/12
to
On 05/17/2012 06:16 AM, BartC wrote:
...
> However, -Wconversion doesn't give a warning in this example:
>
> unsigned int a=4;
> signed int b=-2;
>
> printf("%u<%d = %d\n\n", a, b, a<b);
> printf("%d<%d = %d\n\n", 4, b, 4<b);
> printf("%u<%d = %d\n\n", a, -2, a<-2);
> printf("%d<%d = %d\n\n", 4, -2, 4<-2);
>
> which gives conflicting results. (Presumably an integer literal such as "4"
> is assumed to be signed? Having it as unsigned would be more intuitive, as
> it would be impossible for it to be negative.)

Would you be less confused by the following:

printf("%u<%u = %d\n\n", a, (unsigned)b, a <b);
printf("%u<%u = %d\n\n", 4U, (unsigned)b, 4U<b);
printf("%u<%u = %d\n\n", a, (unsigned)-2, a <-2);
printf("%u<%u = %d\n\n", 4U, (unsigned)-2, 4U<-2);


Tim Rentsch

unread,
May 17, 2012, 6:48:25 PM5/17/12
to
Eric Sosman <eso...@ieee-dot-org.invalid> writes:

> On 5/17/2012 4:40 AM, John Reye wrote:
>> Hello,
>>
>> please can someone explain why
>> (-1< sizeof("test"))
>> is false.
>
> [snip]
>
> Aside: I suppose that on a perverse implementation the
> outcome might be different. If (size_t)-1 is mathematically
> no greater than INT_MAX the conversion would go the other way.
> The size_t would convert to an int before the comparison and
> you'd have:
>
> if ( -1 < (int)sizeof("test") )
>
> I've never heard of an implementation where size_t is so
> narrow, and I'm not 100% sure it would be conforming -- but
> I'm not 100% sure it would be forbidden, either.

More specifically, if the integer conversion rank of size_t is
less than the integer conversion rank of int, and if the two
limits involved satisfy SIZE_MAX <= INT_MAX, then expressions
like '-1 < sizeof x' can be true. For size_t, the Standard
requires only that it be an unsigned integer type and that its
maximum value be at least 65535. So, it is certainly possible
for an implementation to satisfy the listed conditions and
still be conforming.

Eric Sosman

unread,
May 17, 2012, 8:33:32 PM5/17/12
to
On 5/17/2012 8:05 AM, BartC wrote:
> "Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
> news:0.893910e519ad620c6915.2012...@bsb.me.uk...
>> "BartC" <b...@freeuk.com> writes:
>
>>> (Presumably an integer literal such as "4"
>>> is assumed to be signed? Having it as unsigned would be more
>>> intuitive, as
>>> it would be impossible for it to be negative.)
>>
>> But that would turn x < 4 into an unsigned comparison. Worse, -1 would
>> always be unsigned (and large).
>
> 1 would be unsigned.
>
> -1, assuming constant folding by the compiler, would be equivalent to a
> signed integer literal of "-1".

There is no such thing as a "signed integer literal of `-1'."

More exactly, there is no such thing as an "integer literal."
Of course, the "integer-constant" exists and is well-defined, but
even so `-1' is not an "integer-constant."

Quiz question: Write a constant whose type is `int' and whose
value is negative one, without resorting to implementation-defined
or undefined or locale-specific behavior. (Hint: There is at least
one completely portable way to do this.)

> (If not, then it remains the negation of unsigned 1, performed at
> runtime. For this purpose, negating an unsigned value would need to be
> allowed, and I can't see a problem with that, except the usual overflow
> issues).

Negation of unsigned 1 (which can be written `-1u') is already
defined in C, although there are implementation-defined aspects.
In particular, there are no "overflow issues," usual or otherwise.

BartC, your whinings about C and your ideas of how to improve
it would be far more credible if there were evidence that you knew
some C in the first place. Go, and correct that deficiency, and
you will receive more respectful attention than you now merit.

--
Eric Sosman
eso...@ieee-dot-org.invalid

Eric Sosman

unread,
May 17, 2012, 8:40:57 PM5/17/12
to
On 5/17/2012 10:04 AM, James Kuyper wrote:
> On 05/17/2012 07:38 AM, Eric Sosman wrote:
> ...
>> outcome might be different. If (size_t)-1 is mathematically
>> no greater than INT_MAX the conversion would go the other way.
>> The size_t would convert to an int before the comparison and
>> you'd have:
>>
>> if ( -1< (int)sizeof("test") )
>>
>> I've never heard of an implementation where size_t is so
>> narrow, and I'm not 100% sure it would be conforming -- but
>> I'm not 100% sure it would be forbidden, either.
>
> Can you give a justification for your doubts about whether such an
> implementation could be conforming?

Just a reluctance to assert the contradiction, and then come
a cropper when a language lawyer combines the fourth sentence of
6.2.6.3p5 with the second of 5.1.3.2p4 and the entirety of 4.4 to
show that one or the other conclusion is definite.

In short, laziness.

> The lower limit for SIZE_MAX is 65535, and there's no upper limit for
> INT_MAX, so I don't see why an implementation where SIZE_MAX< INT_MAX
> could not be fully conforming.

Nor do I, but "meddle not in the affairs of Standards, for they
are subtle and quick to anger." I have in the past asserted "the
Standard requires" or "the Standard forbids" only to be shown wrong,
so I now try to refrain from such assertions when they're not central
to the matter at hand. Trying to avoid unforced errors, if you like.

--
Eric Sosman
eso...@ieee-dot-org.invalid

Kaz Kylheku

unread,
May 17, 2012, 10:48:57 PM5/17/12
to
On 2012-05-18, Eric Sosman <eso...@ieee-dot-org.invalid> wrote:
> Quiz question: Write a constant whose type is `int' and whose
> value is negative one, without resorting to implementation-defined
> or undefined or locale-specific behavior. (Hint: There is at least
> one completely portable way to do this.)

You didn't say "integer constant". In the grammar, a constant
generates one of four productions, one of which is "enumeration constant".

Hence:

enum x { y = -1; };

y; // <- constant, type int, value -1.

Eric Sosman

unread,
May 17, 2012, 11:15:36 PM5/17/12
to
On 5/17/2012 10:48 PM, Kaz Kylheku wrote:
> On 2012-05-18, Eric Sosman<eso...@ieee-dot-org.invalid> wrote:
>> Quiz question: Write a constant whose type is `int' and whose
>> value is negative one, without resorting to implementation-defined
>> or undefined or locale-specific behavior. (Hint: There is at least
>> one completely portable way to do this.)
>
> You didn't say "integer constant".

Indeed, I did not. That's what's known as a Clue.

> In the grammar, a constant
> generates one of four productions, one of which is "enumeration constant".

Give the man a cigar!

As far as I know, an enum named constant is the only portable
way to produce a negative integer constant in C. On some systems,
constructs like '£' or 'pound' or L'sterling' might be negative
constants, but they're non-portable. (I believe that '\377' must
be positive, although some pre-Standard compilers I dimly recall
made it negative, IIDimlyRC.)

--
Eric Sosman
eso...@ieee-dot-org.invalid

BartC

unread,
May 18, 2012, 7:04:15 AM5/18/12
to
"Eric Sosman" <eso...@ieee-dot-org.invalid> wrote in message
news:jp45cv$l0q$1...@dont-email.me...
> On 5/17/2012 8:05 AM, BartC wrote:


>> (If not, then it remains the negation of unsigned 1, performed at
>> runtime. For this purpose, negating an unsigned value would need to be
>> allowed, and I can't see a problem with that, except the usual overflow
>> issues).

> Negation of unsigned 1 (which can be written `-1u') is already
> defined in C, although there are implementation-defined aspects.
> In particular, there are no "overflow issues," usual or otherwise.

That's true; the value of -3000000000u on my 32-bit C is well-defined;
completely wrong, but well-defined according to the Standard.

Actually only lcc-win32, out of my handful of C compilers, bothers to tell
me that that expression has an overflow.

> BartC, your whinings about C and your ideas of how to improve
> it would be far more credible if there were evidence that you knew
> some C in the first place. Go, and correct that deficiency, and
> you will receive more respectful attention than you now merit.

The 'whinings' were to do with being dependent on compiler options for
figuring why programs like this:

unsigned int a=4;
signed int b=-2;

printf("%u<%d = %d\n", a, b, a<b);
printf("%d<%d = %d\n", 4, b, 4<b);
printf("%u<%d = %d\n", a, -2, a<-2);
printf("%d<%d = %d\n", 4, -2, 4<-2);

(notice the integer literals, or constants, or whatever you like to call
them today, have been correctly displayed as signed values) produce output
like this:

4<-2 = 1
4<-2 = 0
4<-2 = 1
4<-2 = 0

You don't need to know any C, or any language, for it to raise eyebrows. And
as it happened, I had trouble getting any of my four compilers to give any
warning, until someone told me to try -Wextra on gcc.

> BartC, your whinings about C and your ideas of how to improve
> it would be far more credible if there were evidence that you knew
> some C in the first place.

How much C does someone need to know, to complain about -1 being silently
converted to something like 4294967295?

A lot of my 'whinings' are backed up by people who know the language
inside-out. And although nothing can be done because the Standard is always
right, and the language is apparently set in stone, at least discussion
about various pitfalls can increase awareness.

--
Bartc

--
Bartc

James Kuyper

unread,
May 18, 2012, 8:24:12 AM5/18/12
to
On 05/18/2012 07:04 AM, BartC wrote:
> "Eric Sosman" <eso...@ieee-dot-org.invalid> wrote in message
> news:jp45cv$l0q$1...@dont-email.me...
>> On 5/17/2012 8:05 AM, BartC wrote:
...
> That's true; the value of -3000000000u on my 32-bit C is well-defined;
> completely wrong, but well-defined according to the Standard.

The standard defines what "right" means in the context of C code. If
-3000000000u has the value which the standard says it must have, then it
is the right value, even if it's not the value you think the standard
should specify.

...
> unsigned int a=4;
> signed int b=-2;
>
> printf("%u<%d = %d\n", a, b, a<b);
> printf("%d<%d = %d\n", 4, b, 4<b);
> printf("%u<%d = %d\n", a, -2, a<-2);
> printf("%d<%d = %d\n", 4, -2, 4<-2);
>
> (notice the integer literals, or constants, or whatever you like to call
> them today, have been correctly displayed as signed values) produce output
> like this:
>
> 4<-2 = 1
> 4<-2 = 0
> 4<-2 = 1
> 4<-2 = 0
>
> You don't need to know any C, or any language, for it to raise eyebrows.

You're printing out the values of "b" or -2, rather than the values
which are being compared with "a". a<b does not compare the value of "a"
with the value of "b", it compares it with the value of (unsigned)b.
Similarly for a < -2. You need to know C to realize this fact. If you
print out the actual values being compared, you'll see nothing surprising.

>> BartC, your whinings about C and your ideas of how to improve
>> it would be far more credible if there were evidence that you knew
>> some C in the first place.
>
> How much C does someone need to know, to complain about -1 being silently
> converted to something like 4294967295?

Not much; in fact, the less you know about C, the easier it is to feel
the need to complain about such things.

> A lot of my 'whinings' are backed up by people who know the language
> inside-out. And although nothing can be done because the Standard is always
> right, and the language is apparently set in stone, at least discussion
> about various pitfalls can increase awareness.

The language has not been set in stone; C2011 made a lot of changes.
However, this is a very fundamental feature of the language. The
standard already allows a warning for such code; if your compiler
doesn't provide one, complain to the compiler vendor. The C standard
could mandate a diagnostic only at the cost of rendering the behavior
undefined if the code is compiled and executed despite generation of
that diagnostic. Such a change would break a LOT of code, and would
therefore be unacceptable.
--
James Kuyper

Ben Bacarisse

unread,
May 18, 2012, 8:45:52 AM5/18/12
to
"BartC" <b...@freeuk.com> writes:

> "Eric Sosman" <eso...@ieee-dot-org.invalid> wrote in message
> news:jp45cv$l0q$1...@dont-email.me...
>> On 5/17/2012 8:05 AM, BartC wrote:
>
>
>>> (If not, then it remains the negation of unsigned 1, performed at
>>> runtime. For this purpose, negating an unsigned value would need to be
>>> allowed, and I can't see a problem with that, except the usual overflow
>>> issues).
>
>> Negation of unsigned 1 (which can be written `-1u') is already
>> defined in C, although there are implementation-defined aspects.
>> In particular, there are no "overflow issues," usual or otherwise.
>
> That's true; the value of -3000000000u on my 32-bit C is well-defined;
> completely wrong, but well-defined according to the Standard.

Why is this wrong? Negation preserves the type of the operand which
makes it a closed operation on a group -- it's simple to explain and
mathematically justifiable. Why is your idea of what '-' should mean
for unsigned operands better than anyone else's?

> Actually only lcc-win32, out of my handful of C compilers, bothers to tell
> me that that expression has an overflow.

That's because there is none, but I am not just making a semantic point
about "wrap-around" vs. "overflow". I don't think a compiler should
warn about negating an unsigned value because it makes perfect sense --
both doing it and the result you get. Unsigned arithmetic in C is
arithmetic modulo 2^n, where n is the width of the unsigned type. This
is, in almost every case, exactly what I want it to be. I *want* the
arithmetic operators to have the meaning they do for unsigned integer
types. Singling out unary '-' would make it a mess.

Also, look at the way you expressed this. It sounds very dismissive.
Do you really think all those compiler writers simply thought "let's not
bother to warn about this"? Is it not possible they had sound,
well-supported reasons for writing the compiler they did? There are
more kinds of arithmetic than are dreamt of in your philosophy.

>> BartC, your whinings about C and your ideas of how to improve
>> it would be far more credible if there were evidence that you knew
>> some C in the first place. Go, and correct that deficiency, and
>> you will receive more respectful attention than you now merit.
>
> The 'whinings' were to do with being dependent on compiler options for
> figuring why programs like this:
>
> unsigned int a=4;
> signed int b=-2;
>
> printf("%u<%d = %d\n", a, b, a<b);
> printf("%d<%d = %d\n", 4, b, 4<b);
> printf("%u<%d = %d\n", a, -2, a<-2);
> printf("%d<%d = %d\n", 4, -2, 4<-2);
>
> (notice the integer literals, or constants, or whatever you like to call
> them today, have been correctly displayed as signed values) produce output
> like this:
>
> 4<-2 = 1
> 4<-2 = 0
> 4<-2 = 1
> 4<-2 = 0
>
> You don't need to know any C, or any language, for it to raise
> eyebrows.

No, but it helps. The same goes for all sorts of operations in all
programming languages. None of them are free of surprises for those not
in the know. Does C have more scope for surprise? Probably, but it's
not certain. Haskell, for example, has operators that are as close to
what you want as is possible, but you might find lots of other things
about it surprising.

In all these complaints you don't present a cohesive alternative. C's
rules for determining a common type are (despite the presentation of
them in the standard) relatively simple. They stem from a design that
tries to keep close to most hardware can do without extra hidden costs.

> as it happened, I had trouble getting any of my four compilers to give any
> warning, until someone told me to try -Wextra on gcc.

That's another issue altogether. One of the first things I do when
using any compiler is to find out how to get it to tell me as much as
possible about my code. I always plan to shut it up later when I'm
more familiar with the language, but I rarely do.

>> BartC, your whinings about C and your ideas of how to improve
>> it would be far more credible if there were evidence that you knew
>> some C in the first place.
>
> How much C does someone need to know, to complain about -1 being silently
> converted to something like 4294967295?

You are mixing issues again. C does not say the conversion must be
silent and most compilers will help you out here. If you don't like how
they do that, complain to the compiler writers.

As for what C does say, that in signed to unsigned comparisons -1 is
(usually) converted to unsigned, I think some understanding of C is
needed for the complaint to be taken seriously. I would only complain
about language feature X if I was pretty sure I knew why it was designed
that way (even if that turns out to be simply "the designer made a
mistake") and I could demonstrate a better design that meets the same
objectives. I am not saying that people who don't know C can't express
an opinion about it, but that's not the same as declaring a language
feature "wrong" or suggesting that it's obvious that it should be done
differently.

No hardware that I know can compare all signed and unsigned integers in
the way that you seem to want. Something has to give -- either more
code has to be generated or some surprising cases will occur.
Explaining what you want need not be a lot of work. You could just say
C should do it like X does it. Surely at least one programming language
as got integer arithmetic and comparison right as far as you are
concerned.

<snip>
--
Ben.

BartC

unread,
May 18, 2012, 10:17:31 AM5/18/12
to
"Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
news:0.2e9a0e60743c3a0379f8.2012...@bsb.me.uk...

>> That's true; the value of -3000000000u on my 32-bit C is well-defined;
>> completely wrong, but well-defined according to the Standard.
>
> Why is this wrong? Negation preserves the type of the operand which
> makes it a closed operation on a group -- it's simple to explain and
> mathematically justifiable. Why is your idea of what '-' should mean
> for unsigned operands better than anyone else's?

My idea is that when you take a positive number, and negate it, that you
usually end up with a negative number! My apologies if that sounds too
far-fetched!

Translated into a language with a simple type system like C's, that would
imply that applying negation to an unsigned type, would necessarily need to
yield a signed result. Unless you apply a totally different meaning to
negation than is usually understood.

>> How much C does someone need to know, to complain about -1 being silently
>> converted to something like 4294967295?
>
> You are mixing issues again. C does not say the conversion must be
> silent and most compilers will help you out here. If you don't like how
> they do that, complain to the compiler writers.
>
> As for what C does say, that in signed to unsigned comparisons -1 is
> (usually) converted to unsigned, I think some understanding of C is
> needed for the complaint to be taken seriously. I would only complain
> about language feature X if I was pretty sure I knew why it was designed
> that way (even if that turns out to be simply "the designer made a
> mistake") and I could demonstrate a better design that meets the same
> objectives.

(I have a fledgling language and compiler, which while it doesn't do much at
present, can at least take:

println -3000000000

and output -3000000000. The type of 3000000000 is 32-bit unsigned. The type
of -3000000000 is 64-bit signed. I'm not interested in selling or making
available this language, but it's just serving here as an example of a
slightly different approach to doing things.)

> No hardware that I know can compare all signed and unsigned integers in
> the way that you seem to want. Something has to give -- either more
> code has to be generated or some surprising cases will occur.
> Explaining what you want need not be a lot of work. You could just say
> C should do it like X does it. Surely at least one programming language
> as got integer arithmetic and comparison right as far as you are
> concerned.

Hardware either does sign-less add/subtract, or offers signed/unsigned
multiply/divide. So it's mostly a language issue. Since this last has
already been sorted out for C, this is just for interest.

Using fixed-width types for operands and results, there are always going to
be some problems when doing arithmetic: values could overflow for example.
This is acceptable in this context; dealing properly with overflow can be
difficult, you might decide to leave it to a higher level language than
we're dealing with here. (And C decides that unsigned overflow isn't
overflow at all.)

But then you have mixed-signed arithmetic. The proper way to deal with this
is to convert both sides to a common type that can contain both kinds of
value. But this is not always practical (maybe there is no larger type), and
it can be inefficient. So we compromise by keeping the same width, not ideal
because you can't reliably convert one type to the other without possible
overflows. But ignoring overflow for the minute:

You're doing arithmetic between a number that can be negative, on a number
that is positive. The result of course could be negative or positive.
Naturally, you'd expect the result to be a signed type so that it could
accommodate both. But C decides the result is always going to be positive!

Taking 32-bit ints as an example: although there are always going to be
problems with overflows, at least using signed arithmetic, things will work
out if the signed operand is within roughly - 2 billion to 2 billion, the
unsigned one is within 0 to to 2 billion, and the result is within -2
billion to 2 billion. That will include the OP's -1<4 or -1<8 comparison in
addition to many, many ordinary everyday expressions.

But with unsigned arithmetic, both operands and result must lie within 0 to
2 billion, to give a sensible result. There's a strong chance of the wrong
result if *any* negative value, such as -1, is involved. Unless you are
lucky, for example by performing addition, and the result happens to be
positive.

(I did once ask for C's rationale for using unsigned mode for mixed
arithmetic, but I didn't get an answer that explained why.)

--
Bartc

Ben Bacarisse

unread,
May 18, 2012, 11:11:43 PM5/18/12
to
"BartC" <b...@freeuk.com> writes:

> "Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
> news:0.2e9a0e60743c3a0379f8.2012...@bsb.me.uk...
>
>>> That's true; the value of -3000000000u on my 32-bit C is well-defined;
>>> completely wrong, but well-defined according to the Standard.
>>
>> Why is this wrong? Negation preserves the type of the operand which
>> makes it a closed operation on a group -- it's simple to explain and
>> mathematically justifiable. Why is your idea of what '-' should mean
>> for unsigned operands better than anyone else's?
>
> My idea is that when you take a positive number, and negate it, that you
> usually end up with a negative number! My apologies if that sounds too
> far-fetched!

- is not negation, or at least not only negation. That symbol is used
in lots of contexts to mean all sorts of things. In C it means both
negation and "additive inverse" -- the latter being nothing more than
negation in a finite group. In unsigned arithmetic, the additive
inverse is not a negative number (so people shy away from calling it
negation).

The meaning you accept is the one that does not correspond to any usual
mathematical meaning. Signed negation can overflow, making it only an
approximation to the mathematical version. C's unsigned negation always
works and corresponds exactly to the mathematical notion on which it is
modeled.

> Translated into a language with a simple type system like C's, that would
> imply that applying negation to an unsigned type, would necessarily need to
> yield a signed result. Unless you apply a totally different meaning to
> negation than is usually understood.

I don't want to use programming languages that can only perform
operations as they are "usually understood". The "usual" meaning is
already covered by C's signed arithmetic, so C is adding something here.
I've used "scare quotes" because I'm not 100% sure what you mean by
usual. If it's statistical -- ask 100 people and give '-' only the
meaning the majority would expect -- I don't want that to be a factor in
the design of C.

>>> How much C does someone need to know, to complain about -1 being silently
>>> converted to something like 4294967295?
>>
>> You are mixing issues again. C does not say the conversion must be
>> silent and most compilers will help you out here. If you don't like how
>> they do that, complain to the compiler writers.
>>
>> As for what C does say, that in signed to unsigned comparisons -1 is
>> (usually) converted to unsigned, I think some understanding of C is
>> needed for the complaint to be taken seriously. I would only complain
>> about language feature X if I was pretty sure I knew why it was designed
>> that way (even if that turns out to be simply "the designer made a
>> mistake") and I could demonstrate a better design that meets the same
>> objectives.
>
> (I have a fledgling language and compiler, which while it doesn't do much at
> present, can at least take:
>
> println -3000000000
>
> and output -3000000000. The type of 3000000000 is 32-bit unsigned. The type
> of -3000000000 is 64-bit signed. I'm not interested in selling or making
> available this language, but it's just serving here as an example of a
> slightly different approach to doing things.)

That does not give me any clue about why you think C is "wrong". It
tells me what you've done in some language with no context. Does this
language have the same design objectives that C had or had?

>> No hardware that I know can compare all signed and unsigned integers in
>> the way that you seem to want. Something has to give -- either more
>> code has to be generated or some surprising cases will occur.
>> Explaining what you want need not be a lot of work. You could just say
>> C should do it like X does it. Surely at least one programming language
>> as got integer arithmetic and comparison right as far as you are
>> concerned.
>
> Hardware either does sign-less add/subtract, or offers signed/unsigned
> multiply/divide. So it's mostly a language issue. Since this last has
> already been sorted out for C, this is just for interest.
>
> Using fixed-width types for operands and results, there are always going to
> be some problems when doing arithmetic: values could overflow for example.
> This is acceptable in this context; dealing properly with overflow can be
> difficult, you might decide to leave it to a higher level language than
> we're dealing with here. (And C decides that unsigned overflow isn't
> overflow at all.)

You brought up compares (well, the OP did but you extended the example)
and so I talked about compares. It would be simpler to stick to
explaining what C could do that's "better" whilst sticking to C's design
objectives. You've gone off to talk about arithmetic in general. What
do you think C could do with mixed-type compares?

> But then you have mixed-signed arithmetic. The proper way to deal with this
> is to convert both sides to a common type that can contain both kinds of
> value. But this is not always practical (maybe there is no larger type), and
> it can be inefficient. So we compromise by keeping the same width, not ideal
> because you can't reliably convert one type to the other without possible
> overflows. But ignoring overflow for the minute:
>
> You're doing arithmetic between a number that can be negative, on a number
> that is positive. The result of course could be negative or positive.
> Naturally, you'd expect the result to be a signed type so that it could
> accommodate both. But C decides the result is always going to be positive!
>
> Taking 32-bit ints as an example: although there are always going to be
> problems with overflows, at least using signed arithmetic, things will work
> out if the signed operand is within roughly - 2 billion to 2 billion, the
> unsigned one is within 0 to to 2 billion, and the result is within -2
> billion to 2 billion. That will include the OP's -1<4 or -1<8 comparison in
> addition to many, many ordinary everyday expressions.
>
> But with unsigned arithmetic, both operands and result must lie within 0 to
> 2 billion, to give a sensible result. There's a strong chance of the
> wrong result if *any* negative value, such as -1, is involved. Unless
> you are lucky, for example by performing addition, and the result
> happens to be positive.

Where is this going? I know all of the above. I disagree with some of
it (for example that C's unsigned arithmetic produces results that are
not sensible). None of it helps me to see why you think C is wrong.

> (I did once ask for C's rationale for using unsigned mode for mixed
> arithmetic, but I didn't get an answer that explained why.)

--
Ben.

BartC

unread,
May 19, 2012, 6:14:29 AM5/19/12
to
"Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
news:0.4ea130525949b1bfcc18.2012...@bsb.me.uk...
> "BartC" <b...@freeuk.com> writes:

>> My idea is that when you take a positive number, and negate it, that you
>> usually end up with a negative number!

> - is not negation, or at least not only negation. That symbol is used
> in lots of contexts to mean all sorts of things. In C it means both
> negation and "additive inverse" -- the latter being nothing more than
> negation in a finite group. In unsigned arithmetic, the additive
> inverse is not a negative number (so people shy away from calling it
> negation).

You mean like this:

unsigned int a,b;

a=1;
b=-a;

printf("A=%u\n",a);
printf("B=%u\n",b);
printf("A+B=%u\n",a+b);

producing (for example):

A=1
B=4294967295
A+B=0 /* A+(-A) */ ?

> The meaning you accept is the one that does not correspond to any usual
> mathematical meaning. Signed negation can overflow, making it only an
> approximation to the mathematical version. C's unsigned negation always
> works and corresponds exactly to the mathematical notion on which it is
> modeled.

The trouble is that when you look at why people use unsigned numbers, it's
probably not because they particularly want 'closed sets', they usually just
want an extra bit of precision. If that case they are just ordinary numbers
that don't happen to be negative.

For example the unsigned int result (size_t) of 'sizeof'. Here surely the
thing of interest is the quantity involved, not the bit pattern. If you add
the sizes of two large objects, you might expect the result to sometimes
overflow, and not be smaller than either!

(But overall you're right: the way C has dealt with this is actually quite
neat. Perhaps it wasn't so crazy after all..)

>> [it] can at least take:
>>
>> println -3000000000
>>
>> and output -3000000000. The type of 3000000000 is 32-bit unsigned. The
>> type
>> of -3000000000 is 64-bit signed.

> That does not give me any clue about why you think C is "wrong". It
> tells me what you've done in some language with no context. Does this
> language have the same design objectives that C had or had?

(Yes. It's a version of older languages I used when I couldn't get hold of C
for various practical reasons. (But I had the book..)

It is a bit more open-ended than C in the way expressions are evaluated: in
C, usually the result of an operation has to be shoe-horned into a type
determined by the programmer. This can hide some of it's idiosyncrasies, so
if in my example above, b was signed, or printed with "%d", then it would
magically be 'converted' into the -1 that is expected.

With an open expression as in my print statement, more care is needed by the
language to get the expected results. But programming is simpler; it took a
bit of of trial and error to get my -3000000000 printed in C!
(printf("%lld\n",-3000000000ll);))

> You brought up compares (well, the OP did but you extended the example)
> and so I talked about compares. It would be simpler to stick to
> explaining what C could do that's "better" whilst sticking to C's design
> objectives. You've gone off to talk about arithmetic in general. What
> do you think C could do with mixed-type compares?

Presumably the language could not be changed so that it does something other
than unsigned compare. It that case, perhaps make it much more likely that a
warning is given. It's been mentioned that this a compiler issue, and not a
language one, but I suspect that compilers don't warn, without being
explicitly told to do so (and unleashing a host of other warnings), because
the language doesn't take the issue seriously.

If a positive number is compared with one that is quite likely to be
negative, but you assume that both are positive, then there's a very good
chance the result will be wrong! Especially in the example in the subject
line, where it is *known* it's going to be wrong.

(Well, except using Digital Mars' C compiler, where (-1<sizeof("test")) was
true, and 'size_t' was unsigned from what I could determine. Is it possible
it has a bug because it gives the right answer?)

--
Bartc

James Kuyper

unread,
May 19, 2012, 11:00:52 AM5/19/12
to
On 05/19/2012 06:14 AM, BartC wrote:
> "Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
...
> The trouble is that when you look at why people use unsigned numbers, it's
> probably not because they particularly want 'closed sets', they usually just
> want an extra bit of precision. If that case they are just ordinary numbers
> that don't happen to be negative.

That's a bad reason for making that decision; the existence of people
who make that bad decision should not influence the design of the language.

>> You brought up compares (well, the OP did but you extended the example)
>> and so I talked about compares. It would be simpler to stick to
>> explaining what C could do that's "better" whilst sticking to C's design
>> objectives. You've gone off to talk about arithmetic in general. What
>> do you think C could do with mixed-type compares?
>
> Presumably the language could not be changed so that it does something other
> than unsigned compare. ...

That would break a lot of existing code which was correctly written to
take into consideration the way the language is actually designed to
work. so you're right that it's unlikely to be changed.

> ... It that case, perhaps make it much more likely that a
> warning is given. It's been mentioned that this a compiler issue, and not a
> language one, but I suspect that compilers don't warn, without being
> explicitly told to do so (and unleashing a host of other warnings), because
> the language doesn't take the issue seriously.

Compilers routinely warn about things the standard does not require them
to diagnose. Compiler makers are sensitive to the needs of their users;
the ones who aren't, tend not to have many users. If you want a warning,
let your vendor know.

...
> (Well, except using Digital Mars' C compiler, where (-1<sizeof("test")) was
> true, and 'size_t' was unsigned from what I could determine. Is it possible
> it has a bug because it gives the right answer?)

It can't be a bug because it gives the right answer. It could be a bug
if it give the answer you expected it to give. However, if SIZE_MAX <
INT_MAX, the right answer would be the same as the one you expected it
to give. What are the values of SIZE_MAX and INT_MAX for that
implementation?
--
James Kuyper

Richard Damon

unread,
May 19, 2012, 1:52:37 PM5/19/12
to
On 5/18/12 8:24 AM, James Kuyper wrote:
> The language has not been set in stone; C2011 made a lot of changes.
> However, this is a very fundamental feature of the language. The
> standard already allows a warning for such code; if your compiler
> doesn't provide one, complain to the compiler vendor. The C standard
> could mandate a diagnostic only at the cost of rendering the behavior
> undefined if the code is compiled and executed despite generation of
> that diagnostic. Such a change would break a LOT of code, and would
> therefore be unacceptable.

The language could define another "level" of messages besides just a
single type of "diagnostic". There are several things now in the
language which if the standard had language to allow it to require the
emitting of a "warning" (vs an "error") could help programmers. In many
cases "good" compilers already implement them. One key is that the
implementation should need to define how to distinguish a "warning"
diagnostic from an "error" diagnostic, and allow for possible other
levels of messages (like "informational") which the standard doesn't
define/mandate.

Programs that created an "error" would have undefined behavior if
executed (if that was even possible), while programs which only
generated "warnings" should be able to be executed.

BartC

unread,
May 19, 2012, 2:36:37 PM5/19/12
to
"James Kuyper" <james...@verizon.net> wrote in message
news:jp8cj6$qug$1...@dont-email.me...
> On 05/19/2012 06:14 AM, BartC wrote:

>> The trouble is that when you look at why people use unsigned numbers,
>> it's
>> probably not because they particularly want 'closed sets', they usually
>> just
>> want an extra bit of precision. If that case they are just ordinary
>> numbers
>> that don't happen to be negative.
>
> That's a bad reason for making that decision; the existence of people
> who make that bad decision should not influence the design of the
> language.

I would guess that a lot of people don't know about that. All they know is
that a signed char might range from -128 to 127, and unsigned from 0 to 255.
If they need to store small positive numbers with a maximum above 127 but
below 256, then unsigned char makes sense, and uses half the memory of the
next unsigned type. And - most of the time - it works perfectly well.

And if they choose the 'char' type, then they might not even know if the
type is signed or unsigned. That means they will someday get stung with the
subtle differences between the two types.

>> (Well, except using Digital Mars' C compiler, where (-1<sizeof("test"))
>> was
>> true, and 'size_t' was unsigned from what I could determine. Is it
>> possible
>> it has a bug because it gives the right answer?)
>
> It can't be a bug because it gives the right answer. It could be a bug
> if it give the answer you expected it to give. However, if SIZE_MAX <
> INT_MAX, the right answer would be the same as the one you expected it
> to give. What are the values of SIZE_MAX and INT_MAX for that
> implementation?

SIZE_MAX was 2**32-1 (according to the include files; I don't know what
header that is in); INT_MAX is 2**31-1.

--
Bartc

Eric Sosman

unread,
May 19, 2012, 4:49:59 PM5/19/12
to
Doesn't this suggestion lead to four types of diagnostics?

1) "Errors" whose issuance is required by the Standard, and
which are required to produce translation failure,

2) "Errors" whose issuance is required by the Standard, and
which are allowed (but not required) to produce translation
failure,

3) "Warnings" whose issuance is required by the Standard, and
which are not allowed to produce translation failure, and

4) "Informational messages" whose issuance is entirely optional
(the Standard might not even mention them), and which are not
allowed to cause translation failure.

We already have [1] (one instance), [2] (many instances), and [4]
(an open-ended set); the new feature of your proposal is [3]. Are
you convinced [3] is the proper province of a language Standard?
Even if "yes," I think it would be hard to get universal agreement
on what particular diagnostics should be promoted from [4] to [3].
Note, for example, that some compilers can be told to suppress
specific diagnostics; this shows that the line between [3] and [4]
is indistinct and situational, not easily drawn by a Committee many
miles and years removed from a particular slice of code.

The business of a compiler is to compile if it can and to tell
you potentially useful things it discovers in the process, but
setting policy about the use of its outputs seems to me outside its
proper sphere. There's a natural tendency to dump extra work onto
the compiler, simply because it's always "there" and in plain sight;
people might fail to run lint but they can't avoid the compiler.
But if you've got a people problem you should talk with the problem
people, not with ISO/IEC!

--
Eric Sosman
eso...@ieee-dot-org.invalid

James Kuyper

unread,
May 19, 2012, 7:19:40 PM5/19/12
to
On 05/19/2012 02:36 PM, BartC wrote:
> "James Kuyper" <james...@verizon.net> wrote in message
> news:jp8cj6$qug$1...@dont-email.me...
>> On 05/19/2012 06:14 AM, BartC wrote:
...
>>> (Well, except using Digital Mars' C compiler, where (-1<sizeof("test"))
>>> was
>>> true, and 'size_t' was unsigned from what I could determine. Is it
>>> possible
>>> it has a bug because it gives the right answer?)
>>
>> It can't be a bug because it gives the right answer. It could be a bug
>> if it give the answer you expected it to give. However, if SIZE_MAX <
>> INT_MAX, the right answer would be the same as the one you expected it
>> to give. What are the values of SIZE_MAX and INT_MAX for that
>> implementation?
>
> SIZE_MAX was 2**32-1 (according to the include files; I don't know what
> header that is in); INT_MAX is 2**31-1.

SIZE_MAX is supposed to be defined in <stdint.h>; if you're using a C90
compiler, substitute (size_t)-1.
With those values for SIZE_MAX and INT_MAX, having -1 < sizeof "test"
yield a value of true makes the implementation non-conforming. This is
sufficiently unusual that I wonder if you'd be willing to perform some
tests? What does if give if you calculate the following values, convert
them to unsigned long, and print them using "%lu"?

(size_t)-1
sizeof "test"
-1 + sizeof "test"
INT_MAX
SIZE_MAX
--
James Kuyper

BartC

unread,
May 19, 2012, 8:02:39 PM5/19/12
to
"James Kuyper" <james...@verizon.net> wrote in message
news:jp99qe$g37$1...@dont-email.me...
> On 05/19/2012 02:36 PM, BartC wrote:
>> "James Kuyper" <james...@verizon.net> wrote in message
>> news:jp8cj6$qug$1...@dont-email.me...
>>> On 05/19/2012 06:14 AM, BartC wrote:
> ...
>>>> (Well, except using Digital Mars' C compiler, where (-1<sizeof("test"))
>>>> was
>>>> true, and 'size_t' was unsigned from what I could determine. Is it
>>>> possible
>>>> it has a bug because it gives the right answer?)

>> SIZE_MAX was 2**32-1 (according to the include files; I don't know what
>> header that is in); INT_MAX is 2**31-1.
>
> SIZE_MAX is supposed to be defined in <stdint.h>; if you're using a C90
> compiler, substitute (size_t)-1.
> With those values for SIZE_MAX and INT_MAX, having -1 < sizeof "test"
> yield a value of true makes the implementation non-conforming. This is
> sufficiently unusual that I wonder if you'd be willing to perform some
> tests? What does if give if you calculate the following values, convert
> them to unsigned long, and print them using "%lu"?

I used this code:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

int main(void){
printf("(size_t)-1 %lu\n", (unsigned long)((size_t)-1));
printf("sizeof \"test\" %lu\n",(unsigned long)(sizeof "test"));
printf("-1+sizeof \"test\" %lu\n",(unsigned long)(-1 + sizeof "test"));
printf("INT_MAX %lu\n", (unsigned long)INT_MAX);
printf("SIZE_MAX %lu\n", (unsigned long)SIZE_MAX);
printf("-1<sizeof \"test\" %lu\n",(unsigned long)(-1<sizeof "test"));
}

On gcc, it gave this output:

(size_t)-1 4294967295
sizeof "test" 5
-1+sizeof "test" 4
INT_MAX 2147483647
SIZE_MAX 4294967295
-1<sizeof "test" 0

On DMC (Digital Mars' C) it gives this:

(size_t)-1 4294967295
sizeof "test" 5
-1+sizeof "test" 4
INT_MAX 2147483647
SIZE_MAX 4294967295
-1<sizeof "test" 1

Exactly the same, exact for the final compare.

(BTW, why does sizeof "test" give 5? I thought that was the size of the
pointer. It can't be the string length (plus terminator), as it always gives
5 no matter how long the string.)

--
Bartc

pete

unread,
May 19, 2012, 8:44:41 PM5/19/12
to
BartC wrote:

> (BTW, why does sizeof "test" give 5?
> I thought that was the size of the pointer.

No, you didn't.
You were aware that the operand of the sizeof operator,
is one of the exceptions where an expression of array type
is not converted to a pointer.

--
pete

Ben Bacarisse

unread,
May 19, 2012, 8:49:23 PM5/19/12
to
"BartC" <b...@freeuk.com> writes:
<snip>
I won't interrupt James's question by replying here.

> (BTW, why does sizeof "test" give 5? I thought that was the size of
> the pointer.

It's an exception: 6.3.2 p3:

"Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type 'array of type' is converted to an
expression with type 'pointer to type'..."

> It can't be the string length (plus terminator), as it
> always gives 5 no matter how long the string.)

It should be the size of the array that the string gives rise to, so it
should vary with the string's length:

#include <stdio.h>

int main(void)
{
printf("%zu\n", sizeof "a");
printf("%zu\n", sizeof "abc");
printf("%zu\n", sizeof "abacadabara");
}

which produces:

2
4
12

--
Ben.

Eric Sosman

unread,
May 19, 2012, 9:07:47 PM5/19/12
to
Obvious bug in the second implementation.

> (BTW, why does sizeof "test" give 5? I thought that was the size of the
> pointer. It can't be the string length (plus terminator), as it always
> gives 5 no matter how long the string.)

Nonsense: It would not give 5 for `sizeof "BartC"', for example,
nor for `sizeof "Trolling? Who? Me?"'. Have you not read or at least
skimmed the Standard? Any version at all? Have you not read or at
least skimmed K&R -- again, any version at all? Have you never heard
of the FAQ?

"Go, and never darken my towels again." -- G. Marx

--
Eric Sosman
eso...@ieee-dot-org.invalid

Richard Damon

unread,
May 20, 2012, 12:16:52 AM5/20/12
to
The reason to want to have a warning class of diagnostics in my mind
would be most useful to allow more feature that clutter the language to
depreciated status. For example, a rule that a warning shall be
generated for any program that uses trigraphs, unless the first use of
them is the sequence ??= or an option has been provided to the
translator signifying that trigraphs are expected.

It might even be able to define an official way (likely with _Pragma() )
to mark a function as depreciated, and any use of that function shall
generate a warning. Again, with a possible wording to allow the user to
disable such a warning, although the implementation could provide, and
already generally does provide, such an ability anyway, it just would
become non-conforming with such an unauthorized option (but that hasn't
stopped many compilers to have many non-conforming configurations).

As for your grouping of messages, I would NOT advice the standard
distinguishing between 1 and 2 the way you have, if anything the
difference should be on if the implementations HAS generated a
translation result (not just on if it was allowed to), with the
disclaimer that any use of such a result is undefined behavior.

As to level 3 and 4, from my experience their is a distinction of
optional messages between "warning" and "informational" in that the
former implies that the program, while still "legal" in the sense of the
standard, is likely to not do what the programmer thinks it ought to be
doing, while the later messages convey information about the program
without an implication that there may be a problem, being things like
output size, compilation time, or other statistics about the program.
The warning level could be divided by those diagnostics required by the
standard, and those not required, but I am not sure that really adds
much, and it says that if the standard requires documenting how to
determine what class a given diagnostic falls into, then if the standard
later adds an additional warning, then the implementations that
previously generated said warning (and their almost surely would be one,
as a test of existing practice) would need to change the message format
in some way to change its classification.

I would agree that requiring a warning diagnostic for a signed/unsigned
comparison operation is probably farther than should be required by the
standard, and many of the warnings that come out of current compilers
are beyond what should be expected out of a "basic but conforming" C
compiler.

Nomen Nescio

unread,
May 20, 2012, 4:22:45 AM5/20/12
to
"BartC" <b...@freeuk.com> wrote:

> "Eric Sosman" <eso...@ieee-dot-org.invalid> wrote in message
> news:jp45cv$l0q$1...@dont-email.me...
> > On 5/17/2012 8:05 AM, BartC wrote:
>
>
> >> (If not, then it remains the negation of unsigned 1, performed at
> >> runtime. For this purpose, negating an unsigned value would need to be
> >> allowed, and I can't see a problem with that, except the usual overflow
> >> issues).
>
> > Negation of unsigned 1 (which can be written `-1u') is already
> > defined in C, although there are implementation-defined aspects.
> > In particular, there are no "overflow issues," usual or otherwise.
>
> That's true; the value of -3000000000u on my 32-bit C is well-defined;
> completely wrong, but well-defined according to the Standard.
>
> Actually only lcc-win32, out of my handful of C compilers, bothers to tell
> me that that expression has an overflow.
>
> > BartC, your whinings about C and your ideas of how to improve
> > it would be far more credible if there were evidence that you knew
> > some C in the first place. Go, and correct that deficiency, and
> > you will receive more respectful attention than you now merit.
>
> The 'whinings' were to do with being dependent on compiler options for
> figuring why programs like this:

I asked about that before elsewhere, why can't/don't C compilers do a better
job of pointing out obvious problems, given various lints have been written
to do just that. It seems so obvious to me that logic should be included in
a compiler worthy of the name. I was told to go fuck myself. I didn't, but I
understood I was treading on another UNIX golden calf so..

>
> unsigned int a=4;
> signed int b=-2;
>
> printf("%u<%d = %d\n", a, b, a<b);
> printf("%d<%d = %d\n", 4, b, 4<b);
> printf("%u<%d = %d\n", a, -2, a<-2);
> printf("%d<%d = %d\n", 4, -2, 4<-2);
>
> (notice the integer literals, or constants, or whatever you like to call
> them today, have been correctly displayed as signed values) produce output
> like this:
>
> 4<-2 = 1
> 4<-2 = 0
> 4<-2 = 1
> 4<-2 = 0
>
> You don't need to know any C, or any language, for it to raise eyebrows. And
> as it happened, I had trouble getting any of my four compilers to give any
> warning, until someone told me to try -Wextra on gcc.

I don't know any C but it did raise my eyebrows. Looking into this a little:

#include <stdio.h>

int main() {
unsigned int a = 4;
signed int b = -2;

printf("%u<%d = %d\n", a, b, a<b);
printf("%d<%d = %d\n", 4, b, 4<b);
printf("%u<%d = %d\n", a, -2, a<-2);
printf("%d<%d = %d\n", 4, -2, 4<-2);
}

Works like yours:

./bartc
4<-2 = 1
4<-2 = 0
4<-2 = 1
4<-2 = 0

Agreed, not very helpful. Now let's try:

Solaris lint, comes with the system:

lint bartc.c
(9) warning: suspicious comparison of unsigned with negative constant: op "<"

function returns value which is always ignored
printf

Got one and missed one.

Even better, this:

Splint 3.1.2 --- 23 Nov 2011

bartc.c: (in function main)
bartc.c:7:32: Operands of < have incompatible types (unsigned int, int): a < b
To ignore signs in type comparisons use +ignoresigns
bartc.c:7:32: Format argument 3 to printf (%d) expects int gets boolean: a < b
To make bool and int types equivalent, use +boolint.
bartc.c:7:20: Corresponding format code
bartc.c:8:32: Format argument 3 to printf (%d) expects int gets boolean: 4 < b
bartc.c:8:20: Corresponding format code
bartc.c:9:33: Operands of < have incompatible types (unsigned int, int): a < -2
bartc.c:9:33: Format argument 3 to printf (%d) expects int gets boolean: a < -2
bartc.c:9:20: Corresponding format code
bartc.c:10:33: Format argument 3 to printf (%d) expects int gets boolean:
4 < -2
bartc.c:10:20: Corresponding format code
bartc.c:11:2: Path with no return in function declared to return int
There is a path through a function declared to return a value on which there
is no return statement. This means the execution may fall through without
returning a meaningful result to the caller. (Use -noret to inhibit warning)

Finished checking --- 7 code warnings

Conclusions: C (again) fails the least-surprise test, which is least surprising
since it is a language that just happened in an environment where there was
no premium on doing things right but there was a premium on doing them cheap
and without giving any help to the programmer. Resources were tight and
small and ok was better than big and good. What's the excuse now, in the
21st century? Two thumbs up to splint, btw. Damn fine piece of code.

Any serious C coder probably should use some form of lint or even better, splint.

> How much C does someone need to know, to complain about -1 being silently
> converted to something like 4294967295?

I saw the problem and it is somewhat obvious (I write assembly code for
work) but that doesn't mean everybody gets it right all the time in a big
piece of code. The compiler should be more helpful. And lint should be built
in to every C compiler.

> A lot of my 'whinings' are backed up by people who know the language
> inside-out. And although nothing can be done because the Standard is always
> right, and the language is apparently set in stone, at least discussion
> about various pitfalls can increase awareness.

Yes, C sucks, so why use it? I saw pretty quickly discussing any
shortcomings of UNIX or Linux or C just creates a flamefest, no matter how
shitty or broken all that stuff is. When you start messing with peoples'
religion you're going to get your ass kicked. Although it's hard to say
which is preferable, a trip to the dentist or coding on x86, I guess x86
assembly is preferably to C. At least there aren't any surprises. Lots of
disappointment and gasps of horror, but not real surprises.

BartC

unread,
May 20, 2012, 4:56:12 AM5/20/12
to
"Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
news:0.c8f3b31150105e515b5b.2012...@bsb.me.uk...
> "BartC" <b...@freeuk.com> writes:

>> printf("sizeof \"test\" %lu\n",(unsigned long)(sizeof "test"));

>> (BTW, why does sizeof "test" give 5? I thought that was the size of
>> the pointer.

> It should be the size of the array that the string gives rise to, so it
> should vary with the string's length:

OK, that was just a silly mistake. (I changed the string in the caption,
not the actual string being measured. But it was quite late at night
here...)

--
Bartc

BartC

unread,
May 20, 2012, 5:48:06 AM5/20/12
to


"Nomen Nescio" <nob...@dizum.com> wrote in message
news:95634f38f6ee0d11...@dizum.com...


> unsigned int a = 4;
> signed int b = -2;
>
> printf("%u<%d = %d\n", a, b, a<b);

> bartc.c:7:32: Operands of < have incompatible types (unsigned int, int): a
> < b
> To ignore signs in type comparisons use +ignoresigns

Interesting error message, suggesting that use of mixed types is an error
here. As far as I know, comparing mixed types is perfectly legal. My
complaint is that, because it works by converting both to unsigned values,
there is a good chance the result will be completely wrong (ie. if one value
happens to be negative).

Actually, about 50% of arbitrary values within the range of INT_MAX, as this
program measures (using 0..RAND_MAX/2, and +/- RAND_MAX/2). Surely this is
too much for a language/implementation to just ignore?

The language sets out what should happen, and that's exactly what it does,
but I'm not sure how useful such compares are. (The explanation, I've been
told, is all results are perfectly correct according to the sound
mathematical principles on which C operates...)

#include <stdio.h>
#include <stdlib.h>

int main(void){
#define N 1000000
int i,sresult,uresult,total=0,matches=0;
unsigned int a;
signed int b;

for (i=1; i<=N; ++i) {
a=rand()/2;
b=rand()-RAND_MAX/2;
sresult=(signed int )a<b;
uresult=a<b;

// printf("Signed/Signed: %6d < %6d = %d\n", (signed int)a,b,sresult);
// printf("Unsigned/signed: %6u < %6d = %d (b=>%u)\n\n", a, b, uresult,
(unsigned int)b);

++total;
matches+=sresult==uresult;
}

printf("Total mixed compares: %d\n",total);
printf("Correct mixed compares: %d
(%.2f%%)\n",matches,100.0*(double)matches/total);
}

(Interestingly, for about 10 minutes I was convinced the program was giving
75% correct compares. Then I noticed I was using "=" instead of "=="...)

--
Bartc

Ben Bacarisse

unread,
May 20, 2012, 7:35:59 AM5/20/12
to
"BartC" <b...@freeuk.com> writes:
<snip>
>> unsigned int a = 4;
>> signed int b = -2;
>>
>> printf("%u<%d = %d\n", a, b, a<b);
>
>> bartc.c:7:32: Operands of < have incompatible types (unsigned int, int): a
>> < b
>> To ignore signs in type comparisons use +ignoresigns
<snip>
> My
> complaint is that, because it works by converting both to unsigned values,
> there is a good chance the result will be completely wrong (ie. if one value
> happens to be negative).
>
> Actually, about 50% of arbitrary values within the range of INT_MAX, as this
> program measures (using 0..RAND_MAX/2, and +/- RAND_MAX/2). Surely this is
> too much for a language/implementation to just ignore?

You haven't backed up your complaint that the results are "completely
wrong" because you haven't addressed my point about C's design goals.

The integer promotions (which are done first, before the type conversion
that so bothers you) in effect tell the programmer that the compiler
reserves to right to put narrow data into "natural width" registers and
to operate on these instead. The type conversion rules for arithmetic
operators tell the programmer that unsigned operations will be used for
mixed-type operations without any further widening of the data. For
most arithmetic operation, on 2's complement machines, this makes no
difference, but it does for compares.

You may view the choice of converting to unsigned as arbitrary but,
either way, the result of the compare will either be "wrong" for half of
the signed values or for half of the unsigned values (if signed compare
were to be used instead). I can only conclude that you'd like... what?
That compares be done my moving the data into the widest registers
available? That makes some type (for which there is no wider register)
a special case. Maybe you'd prefer to leave the data alone and have a
more complex series of instruction be issued so that that answer is
"right" no matter what signs the operands happen to have. In other
words, what is the compiled code for a<b that you'd like to see?

I don't mind if you say that C's goal of being cheap to compile on a
wide range of machines is wrong (I'd disagree, but that's another
matter), but what I don't think you can say is that C's type conversion
rules are wrong without saying what price you want to pay for "better"
ones.

> The language sets out what should happen, and that's exactly what it does,
> but I'm not sure how useful such compares are. (The explanation, I've been
> told, is all results are perfectly correct according to the sound
> mathematical principles on which C operates...)

If you are referring to me, I did not say that. In particular, I did
not say anything about the mathematical principles that C uses for
compares.

> #include <stdio.h>
> #include <stdlib.h>
>
> int main(void){
> #define N 1000000
> int i,sresult,uresult,total=0,matches=0;
> unsigned int a;
> signed int b;
>
> for (i=1; i<=N; ++i) {
> a=rand()/2;
> b=rand()-RAND_MAX/2;
> sresult=(signed int )a<b;
> uresult=a<b;
>
> // printf("Signed/Signed: %6d < %6d = %d\n", (signed int)a,b,sresult);
> // printf("Unsigned/signed: %6u < %6d = %d (b=>%u)\n\n", a, b, uresult,
> (unsigned int)b);
>
> ++total;
> matches+=sresult==uresult;
> }
>
> printf("Total mixed compares: %d\n",total);
> printf("Correct mixed compares: %d
> (%.2f%%)\n",matches,100.0*(double)matches/total);
> }

Programming is fun, but why write a program to calculate something so
predictable? By the way, RAND_MAX need not be INX_MAX so you could have
got even more confusing data. (And don't you indent code?)

> (Interestingly, for about 10 minutes I was convinced the program was giving
> 75% correct compares. Then I noticed I was using "=" instead of "=="...)

--
Ben.

James Kuyper

unread,
May 20, 2012, 7:44:23 AM5/20/12
to
On 05/19/2012 08:02 PM, BartC wrote:
...
> On DMC (Digital Mars' C) it gives this:
>
> (size_t)-1 4294967295
> sizeof "test" 5
> -1+sizeof "test" 4
> INT_MAX 2147483647
> SIZE_MAX 4294967295
> -1<sizeof "test" 1
>
> Exactly the same, exact for the final compare.

I'm fairly certain that DMC is therefore non-conforming. Since you
approve of this result, I doubt that you'd be inclined to file a bug
report, but someone should.
--
James Kuyper

pete

unread,
May 20, 2012, 8:10:17 AM5/20/12
to
Nomen Nescio wrote:
>
> "BartC" <b...@freeuk.com> wrote:

> > unsigned int a=4;
> > signed int b=-2;
> >
> > printf("%u<%d = %d\n", a, b, a<b);
> > printf("%d<%d = %d\n", 4, b, 4<b);
> > printf("%u<%d = %d\n", a, -2, a<-2);
> > printf("%d<%d = %d\n", 4, -2, 4<-2);
> >
> > (notice the integer literals, or constants,
> > or whatever you like to call them today,
> > have been correctly displayed as signed values) produce output
> > like this:
> >
> > 4<-2 = 1
> > 4<-2 = 0
> > 4<-2 = 1
> > 4<-2 = 0
> >
> > You don't need to know any C, or any language,
> > for it to raise eyebrows.

> I don't know any C but it did raise my eyebrows.

Constructs such as (c = c + 1)
should also be eliminated from the language.
How can c be equal to c plus one?
It just doesn't make any sense!

It's almost as if a programming language
were something that needed to be learned
before it could be used properly.

--
pete

BartC

unread,
May 20, 2012, 8:59:58 AM5/20/12
to
"Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
news:0.c40f0e9f7655e5631fd2.2012...@bsb.me.uk...
> "BartC" <b...@freeuk.com> writes:

>> My
>> complaint is that, because it works by converting both to unsigned
>> values,
>> there is a good chance the result will be completely wrong (ie. if one
>> value
>> happens to be negative).

> For
> most arithmetic operation, on 2's complement machines, this makes no
> difference, but it does for compares.

(See below)

> You may view the choice of converting to unsigned as arbitrary but,
> either way, the result of the compare will either be "wrong" for half of
> the signed values or for half of the unsigned values (if signed compare
> were to be used instead).

But the half of the signed values, includes the very useful range from -1
downwards!

With signed compare, *any* value of int will work, but it will only go wrong
when the unsigned operand is above INT_MAX or so, which I would say is not
typical.

And you expect to take special care with using large numbers; you might not
expect to do when comparing 5 with -1 for example.

Using unsigned compares, then *any* negative value of the signed operand
might give the wrong result (and if someone chooses signed types, it might
be because they expect some values below zero!).

> I can only conclude that you'd like... what?
> That compares be done my moving the data into the widest registers
> available? That makes some type (for which there is no wider register)
> a special case.

I'm just saying that using a signed compare, even on the same width, might
have been a more useful choice; all the combinations that cause problems,
will be shifted into the top end of the unsigned operand.

> I don't mind if you say that C's goal of being cheap to compile on a
> wide range of machines is wrong (I'd disagree, but that's another
> matter), but what I don't think you can say is that C's type conversion
> rules are wrong without saying what price you want to pay for "better"
> ones.

Using signed instead of unsigned for mixed arithmetic (promoting one side to
signed) I don't think costs any more (unless sign extension was costly in
the 1970s). Requiring a stern warning (or insisting on explicit casts)
wouldn't have any runtime costs.

You're right that for some arithmetic, sign doesn't come into it; the
machine operation is the same. However, if you try and print the results of
mixed arithmetic as unsigned values, then you will get more surprises that
doing everything as signed (especially if results wanting to be negative; or
doing divide or mod with one side negative).

>> The language sets out what should happen, and that's exactly what it
>> does,
>> but I'm not sure how useful such compares are. (The explanation, I've
>> been
>> told, is all results are perfectly correct according to the sound
>> mathematical principles on which C operates...)
>
> If you are referring to me, I did not say that. In particular, I did
> not say anything about the mathematical principles that C uses for
> compares.

What was mentioned was "closed operations", "finite groups" and "additive
inverses". Also the fact that C uses "modulo arithmetic" for unsigned types
which cannot oveflow. All of which somehow suggest that unsigned types are a
completely different animal from the signed versions, with their own set of
rules.

>> for (i=1; i<=N; ++i) {
>> a=rand()/2;
>> b=rand()-RAND_MAX/2;
...

> Programming is fun, but why write a program to calculate something so
> predictable?

I find probabilities unintuitive; I like to use actual measurements to back
up my surmises.

> (And don't you indent code?)

(I use one-space indentation. But that seems to have to disappeared in your
quoted version.)

--
Bartc

Eric Sosman

unread,
May 20, 2012, 10:48:00 AM5/20/12
to
[Follow-ups set to comp.std.c in view of thread drift.]
A "shall" that defers to a compiler option seems a pretty weak
requirement.

> As for your grouping of messages, I would NOT advice the standard
> distinguishing between 1 and 2 the way you have, if anything the
> difference should be on if the implementations HAS generated a
> translation result (not just on if it was allowed to), with the
> disclaimer that any use of such a result is undefined behavior.

The distinction isn't mine; it's C-as-it-stands, today. The
lone [1] is the diagnostic generated by #error, which (as of C99)
requires the compiler to reject the code. The [2] category covers
all other diagnostics required by the Standard (in C90, even #error
is a [2]), because the compiler is allowed to overlook the problem
and press onward. If it does so the behavior is undefined, with the
usual proviso that an implementation is free to define behaviors for
situations the Standard leaves undefined.

> I would agree that requiring a warning diagnostic for a signed/unsigned
> comparison operation is probably farther than should be required by the
> standard, and many of the warnings that come out of current compilers
> are beyond what should be expected out of a "basic but conforming" C
> compiler.

The phrase "quality of implementation" was used a lot when the
original ANSI Standard was new and people were getting used to it, but
it doesn't crop up as much nowadays. It seems to me a Standard does
well to steer clear of requiring a particular level of QoI -- or even
of suggesting one (look at how many people treated the Standard's
rand() example as prescriptive rather than illustrative). Besides,
I think the debate would become very politicized very quickly: If a
Committee tried to require a behavior that Compiler X exhibits but
Compiler Y does not, the decision might well be driven not by the
merits of the behaviors, but by the clout of the X and Y vendors.
Also, notions of "good practice" evolve more rapidly than Standards
can; we could easily wind up codifying what's already deprecated!

The users will "vote with their feet" in any case; the Standard
should stay out of the way and let the implementors do their own best
to attract foot traffic.

--
Eric Sosman
eso...@ieee-dot-org.invalid

Richard Damon

unread,
May 20, 2012, 2:50:22 PM5/20/12
to
On 5/20/12 8:59 AM, BartC wrote:
>
> Using signed instead of unsigned for mixed arithmetic (promoting one
> side to
> signed) I don't think costs any more (unless sign extension was costly in
> the 1970s). Requiring a stern warning (or insisting on explicit casts)
> wouldn't have any runtime costs.
>
> You're right that for some arithmetic, sign doesn't come into it; the
> machine operation is the same. However, if you try and print the results of
> mixed arithmetic as unsigned values, then you will get more surprises that
> doing everything as signed (especially if results wanting to be
> negative; or doing divide or mod with one side negative).
>

My guess is that the reasoning comes to minimizing undefined behavior.
Note that overflow and such is well defined for unsigned value, but not
for signed values. There is also the fact that the conversion of a
signed to an unsigned value is defined by the standard. For an unsigned
value to signed, it is only defined IF the value is representable.
Converting an unsigned value greater than INT_MAX to an int, is
explicitly undefined behavior.

Also, it is possible better if the "surprises" happen in more common
cases than uncommon ones. The common cases are more apt to be tested,
and the issue found. The uncommon case might get missed in unit testing
and not show up until the program is fielded and cause a strange
behavior at the customer.

If the unsigned number should never get big enough in practice to reach
the problem case, than the question comes why was the value made
unsigned in the first place. One basic principle that I use is that you
do NOT make a variable unsigned, just because it shouldn't have negative
values, but only if you need some of the properties of unsigned arithmetic.

BartC

unread,
May 20, 2012, 5:46:00 PM5/20/12
to
"Richard Damon" <news.x.ri...@xoxy.net> wrote in message
news:jpbedb$i40$1...@dont-email.me...
> On 5/20/12 8:59 AM, BartC wrote:

>> You're right that for some arithmetic, sign doesn't come into it; the
>> machine operation is the same. However, if you try and print the results
>> of
>> mixed arithmetic as unsigned values, then you will get more surprises
>> tha[n] doing everything as signed (especially if results wanting to be
>> negative; or doing divide or mod with one side negative).

> My guess is that the reasoning comes to minimizing undefined behavior.
> Note that overflow and such is well defined for unsigned value, but not
> for signed values.

> There is also the fact that the conversion of a
> signed to an unsigned value is defined by the standard. For an unsigned
> value to signed, it is only defined IF the value is representable.
> Converting an unsigned value greater than INT_MAX to an int, is
> explicitly undefined behavior.

That sounds reasonable. They seem to have imparted almost 'magical'
properties to unsigned types, so that you can do anything you like to them
without ever getting a wrong result, because any result is always correct
even if it is arithmetically wrong.

> If the unsigned number should never get big enough in practice to reach
> the problem case, than the question comes why was the value made
> unsigned in the first place. One basic principle that I use is that you
> do NOT make a variable unsigned, just because it shouldn't have negative
> values, but only if you need some of the properties of unsigned
> arithmetic.

One property that is useful is to store bigger numbers without having to use
a wider type. But you don't necessarily want everything else that comes with
it.

Also, if you're using 'sizeof', then you don't have the choice to make it
signed, even if all the objects in your program are well within the range of
signed int. And with 'char' you might not know if it is signed or unsigned.

--
Bartc

Ben Bacarisse

unread,
May 20, 2012, 5:57:34 PM5/20/12
to
"BartC" <b...@freeuk.com> writes:

> "Ben Bacarisse" <ben.u...@bsb.me.uk> wrote in message
> news:0.c40f0e9f7655e5631fd2.2012...@bsb.me.uk...
>> "BartC" <b...@freeuk.com> writes:
>
>>> My
>>> complaint is that, because it works by converting both to unsigned
>>> values,
>>> there is a good chance the result will be completely wrong (ie. if one
>>> value
>>> happens to be negative).
>
>> For
>> most arithmetic operation, on 2's complement machines, this makes no
>> difference, but it does for compares.
>
> (See below)
>
>> You may view the choice of converting to unsigned as arbitrary but,
>> either way, the result of the compare will either be "wrong" for half of
>> the signed values or for half of the unsigned values (if signed compare
>> were to be used instead).
>
> But the half of the signed values, includes the very useful range from -1
> downwards!

I'm going to stop here. I don't think a discussion of useful halves is
likely to be very enlightening -- especially as the "alternate C" is
likely to be different in other respects that I don't have time to work
though.

> With signed compare, *any* value of int will work, but it will only go wrong
> when the unsigned operand is above INT_MAX or so, which I would say is not
> typical.
>
> And you expect to take special care with using large numbers; you might not
> expect to do when comparing 5 with -1 for example.
>
> Using unsigned compares, then *any* negative value of the signed operand
> might give the wrong result (and if someone chooses signed types, it might
> be because they expect some values below zero!).
>
>> I can only conclude that you'd like... what?
>> That compares be done my moving the data into the widest registers
>> available? That makes some type (for which there is no wider register)
>> a special case.
>
> I'm just saying that using a signed compare, even on the same width, might
> have been a more useful choice; all the combinations that cause problems,
> will be shifted into the top end of the unsigned operand.

They would be undefined rather than just problematic, but presumably
you'd change that as well.
That's indeed what I said. Of the three main kinds of arithmetic that C
can do (floating, signed int and unsigned int) only unsigned arithmetic
has a simple mathematical model.

>>> for (i=1; i<=N; ++i) {
>>> a=rand()/2;
>>> b=rand()-RAND_MAX/2;
> ...
>
>> Programming is fun, but why write a program to calculate something so
>> predictable?
>
> I find probabilities unintuitive; I like to use actual measurements to back
> up my surmises.
>
>> (And don't you indent code?)
>
> (I use one-space indentation. But that seems to have to disappeared in your
> quoted version.)

Curious. Only the for loop had any indentation in the post I saw, but
that single space did indeed vanish in the quoted part.

--
Ben.

James Kuyper

unread,
May 20, 2012, 6:37:45 PM5/20/12
to
On 05/20/2012 05:46 PM, BartC wrote:
> "Richard Damon" <news.x.ri...@xoxy.net> wrote in message
> news:jpbedb$i40$1...@dont-email.me...
>> On 5/20/12 8:59 AM, BartC wrote:
>
>>> You're right that for some arithmetic, sign doesn't come into it; the
>>> machine operation is the same. However, if you try and print the results
>>> of
>>> mixed arithmetic as unsigned values, then you will get more surprises
>>> tha[n] doing everything as signed (especially if results wanting to be
>>> negative; or doing divide or mod with one side negative).
>
>> My guess is that the reasoning comes to minimizing undefined behavior.
>> Note that overflow and such is well defined for unsigned value, but not
>> for signed values.
>
>> There is also the fact that the conversion of a
>> signed to an unsigned value is defined by the standard. For an unsigned
>> value to signed, it is only defined IF the value is representable.
>> Converting an unsigned value greater than INT_MAX to an int, is
>> explicitly undefined behavior.
>
> That sounds reasonable. They seem to have imparted almost 'magical'
> properties to unsigned types, so that you can do anything you like to them
> without ever getting a wrong result, because any result is always correct
> even if it is arithmetically wrong.

There is a "magical" property, but it's a property of the C standard:
namely, that it, by definition, defines what "correct behavior" for an
implementation of C is. It can be internally inconsistent, badly
designed, or poorly written, among other things, but it can't be wrong.
If you don't like that specification, you can use a different language,
or advocate that it be changed.

The C standard has a great many very precise specifications of the
behavior of unsigned integers. An implementation of C that gets any of
those specifications wrong is non-conforming. That's a completely
different issue from the fact that you don't like the specifications
they've made.

Note: "mathematically wrong" would require identification of specific
aspects of math that are supposed to correspond to C operators. Oddly
enough, mathematics is sufficiently broad field of study that it does
encompass concepts that correspond very well with the behavior required
by the C standard; they're just not the concepts you think should be
relevant. The problem is not that the choices made by the C standard are
mathematically wrong, it's only that they're different from the ones you
think they should have made.

>> If the unsigned number should never get big enough in practice to reach
>> the problem case, than the question comes why was the value made
>> unsigned in the first place. One basic principle that I use is that you
>> do NOT make a variable unsigned, just because it shouldn't have negative
>> values, but only if you need some of the properties of unsigned
>> arithmetic.
>
> One property that is useful is to store bigger numbers without having to use
> a wider type. But you don't necessarily want everything else that comes with
> it.

C chose not to provide that option; you get the option of storing bigger
numbers without change of arithmetic features by going from signed char,
to short, to int, to long, to long long; or by going from int_least8_t,
to int_least16_t, to int_least32_t, or int_least64_t (the fact that you
have two independent ways of doing that is a bit of confusion caused by
the necessity of backwards compatibility). The key point is that you can
only add individual bits to the size of variable without changing it's
signedness if it's a bit-field.

> Also, if you're using 'sizeof', then you don't have the choice to make it
> signed, even if all the objects in your program are well within the range of
> signed int.

Just cast the result of sizeof to a signed type that you know to be big
enough. That's what casts are for.

> And with 'char' you might not know if it is signed or unsigned.

If that's important, use signed char or unsigned char. That's what those
types were invented for. You should use plain char only for text
processing, not for storing small numbers.
--
James Kuyper

Richard Damon

unread,
May 20, 2012, 7:50:53 PM5/20/12
to
On 5/20/12 5:46 PM, BartC wrote:
> "Richard Damon" <news.x.ri...@xoxy.net> wrote in message
> news:jpbedb$i40$1...@dont-email.me...
>> On 5/20/12 8:59 AM, BartC wrote:
>
>>> You're right that for some arithmetic, sign doesn't come into it; the
>>> machine operation is the same. However, if you try and print the results
>>> of
>>> mixed arithmetic as unsigned values, then you will get more surprises
>>> tha[n] doing everything as signed (especially if results wanting to be
>>> negative; or doing divide or mod with one side negative).
>
>> My guess is that the reasoning comes to minimizing undefined behavior.
>> Note that overflow and such is well defined for unsigned value, but not
>> for signed values.
>
>> There is also the fact that the conversion of a
>> signed to an unsigned value is defined by the standard. For an unsigned
>> value to signed, it is only defined IF the value is representable.
>> Converting an unsigned value greater than INT_MAX to an int, is
>> explicitly undefined behavior.
>
> That sounds reasonable. They seem to have imparted almost 'magical'
> properties to unsigned types, so that you can do anything you like to them
> without ever getting a wrong result, because any result is always correct
> even if it is arithmetically wrong.
>

They are defined to give the answer that standard computer hardware that
works on base 2 numbers would give. This meets with C goal of being an
efficient language. At the time the language was first being defined, a
similar rule for signed numbers could not be made as there wasn't a
single model for signed arithmetic in use on computers; while two's
complement was common, so was one's complement and signed magnitude.

The only way to get "arithmetically correct" values for mathematical
expressions would be to use an arbitrary precision math package for the
arithmetic.

One of the reasons C does not try to get signed/unsigned comparison
"right" by your rules, is that there exist (to my knowledge) no
processor with an instruction that will natively do the operation. you
can write the code explicitly to make it work out. As an example,
instead of:

int s;
unsigned u;
int flag;
...
flag = s < u;


you need to use

flag = (s < 0) || (s < u);

to get the right answer by your definition. Why this isn't built into
the language?, well it would make comparison operators a great big
exception to the usual arithmetic conversion, and sometimes it is better
to be consistent rather than intuitive, since once you grok the language
specification the inconsistencies become non-intuitive.

>> If the unsigned number should never get big enough in practice to reach
>> the problem case, than the question comes why was the value made
>> unsigned in the first place. One basic principle that I use is that you
>> do NOT make a variable unsigned, just because it shouldn't have negative
>> values, but only if you need some of the properties of unsigned
>> arithmetic.
>
> One property that is useful is to store bigger numbers without having to
> use
> a wider type. But you don't necessarily want everything else that comes
> with
> it.
>
> Also, if you're using 'sizeof', then you don't have the choice to make it
> signed, even if all the objects in your program are well within the
> range of
> signed int. And with 'char' you might not know if it is signed or unsigned.
>

If you know that all of your object size are going to be well withing
the range of signed int, just store them in signed int. Assigning a
size_t to int is not an error, nor is passing an int to a function
declared to take a size_t. The only places this doesn't work is if you
have to pass the address of a size_t to something, and I can't think of
any standard functions that take a size_t* parameter. The other case is
passing to a function like printf which uses var_args.

For combining int-s and unsigned-s to squeeze out that last drop of
range, well, you are going to need to be VERY careful looking over the
operations to make sure you haven't committed overflow. Note that by
your mathematical model there is no appropriate type the compiler can
use for taking the difference of two numbers without going to a larger
type. Since this is inefficient, C won't do that by default, but you can
force it to happen by widening the arguments before performing the
operation.

Using plain char to hold numbers is generally a mistake because of the
unknown sign-ness, if using char sized values to save space, ALWAYS make
them explicitly signed or unsigned.

Nomen Nescio

unread,
May 21, 2012, 11:28:44 AM5/21/12
to
Forwarding this to guys who write code in real languages to see what they
think of this. AFAIK you cannot get something like that past the compiler in
Ada...and you would have to define a type or a subtype to even have an
unsigned int unless you use a modular type IIRC. In FORTRAN I don't remember
an unsigned integer but I haven't used it much since FORTRAN IV.

Basically C gives the coder no help in the example you wrote. It doesn't
make sense to do what you did. It's almost surely an error and it should at
least be flagged as a warning. The fact people calling themselves C
programmers can defend any compiler just letting this go by without at least
a warning amd flaming you and calling you an idiot and a noob really says a
lot about their total lack of discipline and explains the pathetic state of
buffer overflows and race conditions, ad naseum, found in C code..

Richard Maine

unread,
May 21, 2012, 11:37:46 AM5/21/12
to
Nomen Nescio <nob...@dizum.com> wrote:

> Forwarding this to guys who write code in real languages to see what they
> think of this. AFAIK you cannot get something like that past the compiler in
> Ada...and you would have to define a type or a subtype to even have an
> unsigned int unless you use a modular type IIRC. In FORTRAN I don't remember
> an unsigned integer but I haven't used it much since FORTRAN IV.

Correct on the factual question. Standard Fortran has no unsigned kinds.
Some compilers do such a thing as an extension, but as is the nature of
extensions, one cannot guarantee that all compilers would make the same
choices on the fine points.

I won't comment on the rest. The parts you cited already sounded like a
flame fest, and using a term "real languages" doesn't help much. I won't
participate in any discussion that has already degraded to that point.

--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain

Tim Rentsch

unread,
May 21, 2012, 12:59:45 PM5/21/12
to
This seems implicitly to offer two related but distinct suggestions,
namely, that two types of messages ("errors" and "warnings") be able
to be differentiated, and that certain constructions be identified
as requiring warning messages.

As to the first suggestion, this isn't a behavioral change but a
documentational one, ie, that implementations describe not one
subset of message outputs but two. Considered by itself, this
change doesn't seem to offer much value, because any implementation
that goes to the trouble of providing warning messages is likely
also to describe which are which.

As to the second suggestion, IMO the idea of changing the Standard
so that some legal program constructions are required to produce
warning messages, with no effect on program semantics, has nothing
to recommend it.

Kaz Kylheku

unread,
May 21, 2012, 1:05:00 PM5/21/12
to
On 2012-05-20, Nomen Nescio <nob...@dizum.com> wrote:
> I don't know any C but it did raise my eyebrows. Looking into this a little:
>
> #include <stdio.h>
>
> int main() {
> unsigned int a = 4;
> signed int b = -2;
>
> printf("%u<%d = %d\n", a, b, a<b);
> printf("%d<%d = %d\n", 4, b, 4<b);
> printf("%u<%d = %d\n", a, -2, a<-2);
> printf("%d<%d = %d\n", 4, -2, 4<-2);
> }
>
> Works like yours:
>
> ./bartc
> 4<-2 = 1
> 4<-2 = 0
> 4<-2 = 1
> 4<-2 = 0
>
> Agreed, not very helpful. Now let's try:
>
> Solaris lint, comes with the system:
>
> lint bartc.c
> (9) warning: suspicious comparison of unsigned with negative constant: op "<"

You forgot the obvious: compile with "gcc -Wall -W".

test.c: In function ‘main’:
test.c:7:33: warning: comparison between signed and unsigned integer expressions
test.c:9:34: warning: comparison between signed and unsigned integer expressions
test.c:11:1: warning: control reaches end of non-void function

> function returns value which is always ignored
> printf

> Got one and missed one.
>
> Even better, this:

> Splint 3.1.2 --- 23 Nov 2011

This is not better. Splint is not a tool that you can simply download, compile
and then run in this manner. Proper use of Splint requires you to RTFM and then
fine-tune the tool to actually look for problems. Making the most of Splint
requires special annotations in the code.

If you just invoke it this way, you get reams of spewage full of all kinds of
false positive identifications of situations, most of which are are not
erroneous in any way.

--
If you ever need any coding done, I'm your goto man!

Tim Rentsch

unread,
May 21, 2012, 1:13:48 PM5/21/12
to
Eric Sosman <eso...@ieee-dot-org.invalid> writes:

[snip]
>>>
>>> Doesn't this suggestion lead to four types of diagnostics?
>>>
>>> 1) "Errors" whose issuance is required by the Standard, and
>>> which are required to produce translation failure,
>>>
>>> 2) "Errors" whose issuance is required by the Standard, and
>>> which are allowed (but not required) to produce translation
>>> failure,
>
> [snip] The
> lone [1] is the diagnostic generated by #error, which (as of C99)
> requires the compiler to reject the code. The [2] category covers
> all other diagnostics required by the Standard (in C90, even #error
> is a [2]), because the compiler is allowed to overlook the problem
> and press onward. If it does so the behavior is undefined, with the
> usual proviso that an implementation is free to define behaviors for
> situations the Standard leaves undefined. [snip]

Micro-nit: I think #error in C90 is not quite the same as the
[2] category, because all of those are necessarily undefined
behavior. A #error in C90 does not violate any syntax rule or
constraint, nor can I find any other reason it would give rise
to undefined behavior.

Other than the u-nit, very nice analysis.

Tim Rentsch

unread,
May 21, 2012, 1:18:29 PM5/21/12
to
"BartC" <b...@freeuk.com> writes:

[snip, and minor snip later]
Is there a chance that the Digital Mars compiler was
run in a non-conforming mode? I'd be very surprised
if Walter got this wrong.

glen herrmannsfeldt

unread,
May 21, 2012, 2:16:30 PM5/21/12
to
In comp.lang.fortran Nomen Nescio <nob...@dizum.com> wrote:

(snip)

> Basically C gives the coder no help in the example you wrote.
> It doesn't make sense to do what you did. It's almost surely an
> error and it should at least be flagged as a warning.

(snip)
>> > >> (If not, then it remains the negation of unsigned 1, performed at
>> > >> runtime. For this purpose, negating an unsigned value would need to be
>> > >> allowed, and I can't see a problem with that, except the usual overflow
>> > >> issues).

>> > > Negation of unsigned 1 (which can be written `-1u') is already
>> > > defined in C, although there are implementation-defined aspects.
>> > > In particular, there are no "overflow issues," usual or otherwise.

(snip)
>> > Actually only lcc-win32, out of my handful of C compilers,
>> > bothers to tell me that that expression has an overflow.

You should post a bug report for lcc-win32 then.

Unsigned types wrap, and don't overflow. (At least in C89.)

(snip)
>> > unsigned int a=4;
>> > signed int b=-2;

>> > printf("%u<%d = %d\n", a, b, a<b);
>> > printf("%d<%d = %d\n", 4, b, 4<b);
>> > printf("%u<%d = %d\n", a, -2, a<-2);
>> > printf("%d<%d = %d\n", 4, -2, 4<-2);

Mixing signed and unsigned types can give surprising results.
The results are defined, but it is usual for compilers to
give a warning.

Note that C89 (and I believe later) allows signed integer types
to have twos complement, ones complement, or sign magnitude
representation, and that affects some expressions mixing signed
and unsigned values. In those cases, the results are implementation
dependent.

(If anyone writes a C compiler for the 7090 we can check this.)

(snip)
>> Solaris lint, comes with the system:

>> lint bartc.c
>> (9) warning: suspicious comparison of unsigned with negative constant: op "<"

>> function returns value which is always ignored
>> printf

C programmers way too often don't check the return value of I/O
operations. Many I/O errors go unnoticed that way.

(snip)
>> bartc.c:7:32: Format argument 3 to printf (%d) expects int gets boolean: a < b

This is new since C89, but has to be allowed for back compatibility.

As far as I can tell, this is a complaint against unsigned arithmetic
wrapping instead of generating overflow. C89 leaves undefined the
effect of overflow on signed integer expressions. One could still be
surprised in that case.

-- glen

pete

unread,
May 21, 2012, 4:16:05 PM5/21/12
to
glen herrmannsfeldt wrote:

> Note that C89 (and I believe later) allows signed integer types
> to have twos complement, ones complement, or sign magnitude
> representation, and that affects some expressions mixing signed
> and unsigned values. In those cases, the results are implementation
> dependent.

C89 does not specify the representation of negative integer values.

--
pete

BartC

unread,
May 21, 2012, 4:19:22 PM5/21/12
to
"Tim Rentsch" <t...@alumni.caltech.edu> wrote in message
news:kfnmx51...@x-alumni2.alumni.caltech.edu...
> "BartC" <b...@freeuk.com> writes:

>> On DMC (Digital Mars' C) it gives this:

>> -1<sizeof "test" 1

> Is there a chance that the Digital Mars compiler was
> run in a non-conforming mode? I'd be very surprised
> if Walter got this wrong.

I didn't see anything obvious amongst all the options. And I made sure it
was the latest version.

--
Bartc



Nomen Nescio

unread,
May 21, 2012, 6:25:01 PM5/21/12
to
Kaz Kylheku <k...@kylheku.com> wrote:

> On 2012-05-20, Nomen Nescio <nob...@dizum.com> wrote:
> > I don't know any C but it did raise my eyebrows. Looking into this a little:
> >
> > #include <stdio.h>
> >
> > int main() {
> > unsigned int a = 4;
> > signed int b = -2;
> >
> > printf("%u<%d = %d\n", a, b, a<b);
> > printf("%d<%d = %d\n", 4, b, 4<b);
> > printf("%u<%d = %d\n", a, -2, a<-2);
> > printf("%d<%d = %d\n", 4, -2, 4<-2);
> > }
> >
> > Works like yours:
> >
> > ./bartc
> > 4<-2 = 1
> > 4<-2 = 0
> > 4<-2 = 1
> > 4<-2 = 0
> >
> > Agreed, not very helpful. Now let's try:
> >
> > Solaris lint, comes with the system:
> >
> > lint bartc.c
> > (9) warning: suspicious comparison of unsigned with negative constant: op "<"
>
> You forgot the obvious: compile with "gcc -Wall -W".

I guess you're talking to bartc. I don't use gcc.


> test.c: In function ‘main’:
> test.c:7:33: warning: comparison between signed and unsigned integer expressions
> test.c:9:34: warning: comparison between signed and unsigned integer expressions
> test.c:11:1: warning: control reaches end of non-void function
>
> > function returns value which is always ignored
> > printf
>
> > Got one and missed one.
> >
> > Even better, this:
>
> > Splint 3.1.2 --- 23 Nov 2011
>
> This is not better. Splint is not a tool that you can simply download, compile
> and then run in this manner. Proper use of Splint requires you to RTFM and then
> fine-tune the tool to actually look for problems. Making the most of Splint
> requires special annotations in the code.

Fine but I thought most of the people here are such experts in C that they
would have no problems doing that. Since they like outsmarting gcc or
whatever C compiler they use, they surely have extra time for RTFM ;-)

> If you just invoke it this way, you get reams of spewage full of all kinds of
> false positive identifications of situations, most of which are are not
> erroneous in any way.

No doubt. My point was simply there are tools that can find stupid mistakes
and they should probably be used. C is not my thing, but if it's yours I'm
sure you know all the tricks already.





































Terence

unread,
May 21, 2012, 9:08:17 PM5/21/12
to
Unsigned integers can be simple signed integers with enough bits to never
exceed the maximum positive number desired. Phylosophically there is no
difference. It's only the precision overflow than can cause a mathemetical
error in simple arithmetic.

Postings like this (not the integer bit, but the surrounding miasma of
complex code) cause me to ask 'WHY'?

Fortran has become more and more complex as the committees approve new
extensions or features and then vendors comply, and then users are 'forced'
to buy a new version or pay for an update if they want continued support.
And finally coding slows down.

It really doesn't have to be this way.

You've all read my discourses on continuing to use F77. I have my reasons,
even if I do also own a Fortran 90/95 compiler

The only negative that I have met (others might not) to my 1983 F77 compiler
is the limitation on contiguous memory space. I don't need any of the
'advances' beyond that version.
All the file formats I could possibly use are there. I can re-express any
modern Fortran code in an equivalent F77 code except for the contigous
memory requirements. (If I have to, I use work files).
I develop (very quickly indeed) in F77. If the client wants a native Windows
version I just recompile with the F95 one.

I don't have problems with a new setup on a newer computer (I'm now on my
gifted Mac Professional using MS Version 7; three earlier machine still work
fine). How many postings are about actually getting a compiler to work?

I have no problems with compiling, linking or even running on pretty much
any of my programs on any machine. How many postings are about how to do
this, or interpret the errors and warnings that a problematic
compiler-linker setup brings?

If I were to teach (again) any students in Fortran, I would still start with
F77. Only later, would I point out what you get with more recent compilers;
the use of modules and intent definitions; the distinction between public
and private data that is useful for OOPs work (strange abbreviation!), the
shortening of code (and reliability) with matrix-formulated operations and
so on, but...

There are some English dialects where every third or forth word is an
automatic obcenity (e.g. Geordie). I prefer the language versions without
the unnecesary stuff. There's more clarity.





glen herrmannsfeldt

unread,
May 21, 2012, 9:37:22 PM5/21/12
to
As I understand it, it requires the representation of positive
signed and unsigned having the same value to be the same.
And that unsigned look like unsigne binary.
(Within the limits of the "as if" rule.)

The three I indicate are three that have that property.
Note that biased, or offset form does not work.
I suppose one could come up with a signed version that matched
unsigned for positive and was different than the three listed,
but it would have similar properties to those listed.

Specifically, it either does or does not have a negative zero.
In terms of bit operations, that can be pretty important!

-- glen

glen herrmannsfeldt

unread,
May 21, 2012, 9:45:10 PM5/21/12
to
In comp.lang.fortran Nomen Nescio <nob...@dizum.com> wrote:
(snip)
>> > > Negation of unsigned 1 (which can be written `-1u') is already
>> > > defined in C, although there are implementation-defined aspects.
>> > > In particular, there are no "overflow issues," usual or otherwise.

Note that in C this is applying the unary - operator to the
constant 1u.

In Fortran, except in rare cases (the DATA statement being one)
constants are not signed. (The implentation may apply the unary
minus operator before generating the in-memory constant.)

I would usually write ~0u instead of -1u, but the result is
the same in both cases.

In Fortran, one could write NOT(0), with the usual restriction
on the bitwise operators on values with the sign bit set.
(In addition to the fact that Fortran doesn't require a binary
representation for data.)

I don't know how ADA treats signed values, or how its bitwise
operators work. This is still posted to the ADA group, though.

-- glen

pete

unread,
May 21, 2012, 10:16:52 PM5/21/12
to
glen herrmannsfeldt wrote:
>
> In comp.lang.fortran pete <pfi...@mindspring.com> wrote:
> > glen herrmannsfeldt wrote:
>
> >> Note that C89 (and I believe later) allows signed integer types
> >> to have twos complement, ones complement, or sign magnitude
> >> representation, and that affects some expressions mixing signed
> >> and unsigned values. In those cases, the results are implementation
> >> dependent.
>
> > C89 does not specify the representation of negative integer values.
>
> As I understand it, it requires the representation of positive
> signed and unsigned having the same value to be the same.
> And that unsigned look like unsigne binary.
> (Within the limits of the "as if" rule.)
>
> The three I indicate are three that have that property.

The three representations which you indicated,
are representations of negative values.

--
pete

pete

unread,
May 21, 2012, 10:21:54 PM5/21/12
to
You're not wrong about C89 allowing them.
In C99 only those three representations were allowed.
In C89 any representation which used a sign bit was allowed
for negative integer values.

--
pete

Nomen Nescio

unread,
May 22, 2012, 2:11:54 AM5/22/12
to
I'm not an expert on this but Ada is very strongly (statically) typed. There
are only two integer types in Ada to start with, signed integer and modular
integer. The compiler will flag an error if you try to compare variables of
those two types or indeed variables of any differing types. You can define
subtypes of existing types and completely new types and Ada insures you
can't make mistakes by mixing apples and oranges. While this is a bit
confining at times (when you know it's ok to do so) it does preclude the
possibility of something like what Bartc wrote from ever happening. If you
know the conversion is ok there are ways (usually by providing an unchecked
conversion function) to assign or compare across types.










Les Neilson

unread,
May 22, 2012, 3:54:53 AM5/22/12
to

"Terence" <tbwr...@bigpond.net.au> wrote in message
news:KFBur.7119$v14....@viwinnwfe02.internal.bigpond.com...
>
>
> There are some English dialects where every third or forth word is an
> automatic obcenity (e.g. Geordie).
>
On behalf of all the Geordies I know - including myself - I object.
I never heard my parents or grandparents use obscenities. The school friends
I hung around with did not use obscenities (though admittedly there were
some others that did!)
I once used the word Hell, and not in a religious context, and got a rather
disapproving look from my mother.

You and I obviously moved in different circles.
:-)

Les

BartC

unread,
May 22, 2012, 5:55:01 AM5/22/12
to
"Nomen Nescio" <nob...@dizum.com> wrote in message
news:eb42d1e8b4d1e87b...@dizum.com...
> glen herrmannsfeldt <g...@ugcs.caltech.edu> wrote:

>> I don't know how ADA treats signed values, or how its bitwise
>> operators work. This is still posted to the ADA group, though.
>
> I'm not an expert on this but Ada is very strongly (statically) typed.
> There
> are only two integer types in Ada to start with, signed integer and
> modular
> integer. The compiler will flag an error if you try to compare variables
> of
> those two types or indeed variables of any differing types.

Signed and modular is a better way of explaining how C treats these types,
with a clear distinction between them.

Signed types clearly should be the primary way to represent actual integer
quantities as most of us think of them. A signed integer range can represent
any positive value (given enough bits); an unsigned range can't represent
any positive or negative value (not with a finite number of bits anyway).

That's why it's odd that when there is any hint of a unsigned operand, C
should switch to modular arithmetic for both.

But, given that C works at a lower level, and the choice of bit-widths is
limited, then perhaps there ought to be 3 integer types:

- Signed
- New-unsigned
- Modular

With the new-unsigned type behaving as I have suggested a few times [in the
original thread in comp.lang.c], just a subrange of a signed type (for
example, a hypothetical signed integer type that is one bit wider).

With the new-unsigned type, a negation operation must have signed result
(because, other than -0, it will be negative). While subtraction would need
to be signed too, as results can be negative. And of course there is the
likelihood of overflow or underflow, for which I will leave to other
language proposals to deal with..

--
Bartc

Georg Bauhaus

unread,
May 22, 2012, 6:29:59 AM5/22/12
to
On 22.05.12 03:45, glen herrmannsfeldt wrote:

> I don't know how ADA treats signed values, or how its bitwise
> operators work. This is still posted to the ADA group, though.

A feature of Ada (a lady's first name; some older book's
have capitals in titles) is that you would not normally have
to worry; you still can if you like, or must.

First, one cannot store signed (unsigned) values in places whose
type is unsigned (signed) unless using force such as explicit
type conversion, or Unchecked_Conversion. Different types,
no implicit conversions, as mentioned in the thread.

As needed, one selects from the three kinds of types that
have logical operations, Boolean, modular, and packed array
of Boolean.

Some redundant assertions in the following.

with Interfaces; -- for unsigned modular "hardware" types
with Ada.Unchecked_Conversion;
procedure Bops is

-- modular types with modular arithmetic and Boolean ops:

type U8 is mod 2**8;

pragma Assert (U8'Pred (0) = -1);
pragma Assert (U8'Succ (U8'Last) = 0);

X8 : U8 := 2#1111_1111#;
--X9 : U8 := 2#1111_1111_1#; -- compiler rejects, value not in range
X1 : U8 := -1; -- modular arithmetic
X0 : U8 := not 0; -- has Boolean ops

pragma Assert (X0 = X1);
pragma Assert (X1 = 2#1111_1111#);

-- convert from signed:

type S8 is range -128 .. 127;
for S8'Size use 8;

function To_U8 is new Ada.Unchecked_Conversion (S8, U8);
I8 : S8 := -1; -- a negative signed value
--XI : U8 := U8 (I8); -- type conv will raise! value out of range
XU : U8 := To_U8 (I8); -- o.K., not checked

pragma Assert (XU = 8#377#);

-- unsinged_N "hardware types" when supported by the compiler;
-- includes shifting operations and such, is modular

use type Interfaces.Unsigned_64; -- import "+" for literals
U64 : Interfaces.Unsigned_64 := 16#FFFF_FFFF_FFFF_0000# + 2#101#;


-- types for convenient access to individual bits

type Bitvec is array (Natural range <>) of Boolean;
pragma Pack (Bitvec); -- guaranteed
subtype Bitvec_8 is Bitvec (0 .. 7);

Y : Bitvec (0 .. 11) := (5 => True, others => False);

pragma Assert (Y(5) and not Y(11));

Z : Bitvec_8;
Toggle : constant Bitvec_8 := (others => True);
begin
Y (11) := True;
Z := Y(8 - 6 + 1 .. 8) & Y(10 .. 11) xor Toggle;
pragma Assert (Z = (True, True, False, True,
True, True, True, False));
end Bops;


-- Georg

Dmitry A. Kazakov

unread,
May 22, 2012, 8:07:05 AM5/22/12
to
On Tue, 22 May 2012 10:55:01 +0100, BartC wrote:

> With the new-unsigned type behaving as I have suggested a few times [in the
> original thread in comp.lang.c], just a subrange of a signed type (for
> example, a hypothetical signed integer type that is one bit wider).
>
> With the new-unsigned type, a negation operation must have signed result
> (because, other than -0, it will be negative). While subtraction would need
> to be signed too, as results can be negative. And of course there is the
> likelihood of overflow or underflow, for which I will leave to other
> language proposals to deal with..

Integer arithmetic is more than just + and -. Did you ask yourself what is
the motivation to have it one bit wider? The answer is to have + and -
closed. What about *? That would make it x2 bits wider. What about
exponentiation? Much more bits. Factorial?

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

gwowen

unread,
May 22, 2012, 8:14:32 AM5/22/12
to
On May 20, 11:37 pm, James Kuyper <jameskuy...@verizon.net> wrote:

> Note: "mathematically wrong" would require identification of specific
> aspects of math that are supposed to correspond to C operators. Oddly
> enough, mathematics is sufficiently broad field of study that it does
> encompass concepts that correspond very well with the behavior required
> by the C standard; they're just not the concepts you think should be
> relevant. The problem is not that the choices made by the C standard are
> mathematically wrong, it's only that they're different from the ones you
> think they should have made.

While there's a lot of truth to that, the problem is that the C
language defines operators on those abstractions that no mathematician
would.

For arithmetic (+,-,* and [maybe] /) the unsigned integers model Z_N
for some large value of N. That's all fine and dandy, but no sane
mathematician attempts to consider Z_N to be an ordered field, because
there aren't any total orderings that behave sanely w.r.t. addition or
multiplication.

So the unsigned integers aren't just "not the mathematical concept we
think", they're a solidly defined mathematical concept, with a load of
horribly inappropriate concepts bolted on, whose behaviour is far more
closely related to how silicon works, rather than how mathematics
works.

So as soon as we use comparison operators, the "its just a closed
group" argument falls utterly to pieces.

Fritz Wuehler

unread,
May 22, 2012, 8:23:09 AM5/22/12
to
pete <pfi...@mindspring.com> wrote:

> Nomen Nescio wrote:
> >
> > "BartC" <b...@freeuk.com> wrote:
>
> > > unsigned int a=4;
> > > signed int b=-2;
> > >
> > > printf("%u<%d = %d\n", a, b, a<b);
> > > printf("%d<%d = %d\n", 4, b, 4<b);
> > > printf("%u<%d = %d\n", a, -2, a<-2);
> > > printf("%d<%d = %d\n", 4, -2, 4<-2);
> > >
> > > (notice the integer literals, or constants,
> > > or whatever you like to call them today,
> > > have been correctly displayed as signed values) produce output
> > > like this:
> > >
> > > 4<-2 = 1
> > > 4<-2 = 0
> > > 4<-2 = 1
> > > 4<-2 = 0
> > >
> > > You don't need to know any C, or any language,
> > > for it to raise eyebrows.
>
> > I don't know any C but it did raise my eyebrows.
>
> Constructs such as (c = c + 1)
> should also be eliminated from the language.
> How can c be equal to c plus one?
> It just doesn't make any sense!

Uhm no. It makes sense because it's algebraic and that's a system everyone
is familiar with. I guess you feel kinda stupid now because you just proved
bartc's point. C's behavior fails least surprise and is not intuitive or
helpful.

Everybody who understands algebra can understand what c = c + 1 means. What
they can't understand unless they think about *implementation* and C's lack
of helpful warnings *as a default in most compilers* is how 4 can be less
than -2.

> It's almost as if a programming language
> were something that needed to be learned
> before it could be used properly.

It's almost as if programming languages should be implemented by compiler
writers to provide least surprise, and reasonable warnings. It's obvious
that bartc's code should have been flagged. If somebody is comparing a
signed and unsigned value that is usually a mistake. What was your point
again? Oh yeah, the standard, the standard...

Fritz Wuehler

unread,
May 22, 2012, 11:43:16 AM5/22/12
to
Thanks Terence. What you say goes against the neverending drive by most
technology people to constantly change things that are mostly fine, and to
think new is and old is. I agree with your sentiments.

> I prefer the language versions without the unnecesary stuff. There's more
> clarity.

Sadly, I find most of modern Fortran unreadable whereas I found FORTRAN (at
the time) perfectly readable.

BartC

unread,
May 22, 2012, 12:25:17 PM5/22/12
to


"Dmitry A. Kazakov" <mai...@dmitry-kazakov.de> wrote in message
news:15puwddz4h6cl$.34i9lxveafeb.dlg@40tude.net...
> On Tue, 22 May 2012 10:55:01 +0100, BartC wrote:
>
>> With the new-unsigned type behaving as I have suggested a few times [in
>> the
>> original thread in comp.lang.c], just a subrange of a signed type (for
>> example, a hypothetical signed integer type that is one bit wider).
>>
>> With the new-unsigned type, a negation operation must have signed result
>> (because, other than -0, it will be negative). While subtraction would
>> need
>> to be signed too, as results can be negative. And of course there is the
>> likelihood of overflow or underflow, for which I will leave to other
>> language proposals to deal with..
>
> Integer arithmetic is more than just + and -. Did you ask yourself what is
> the motivation to have it one bit wider?

Yes. So that given 32 bits for example, we can count to approximately 4
billion instead of 2 billion.

> The answer is to have + and -
> closed. What about *? That would make it x2 bits wider. What about
> exponentiation? Much more bits. Factorial?

We're talking about C where operations and their results usually have the
same predefined width. If it was necessary to worry about worst-cases on
every operation, that would make programming at this level pretty much
impossible.

Instead, that sort of auto-ranging, multi-precision utility is left to
higher-level languages, and languages such as C are used to implement them.

For that purpose, full-width unsigned arithmetic, preferably with carry
status, as available in most processors, is extremely useful.

--
Bartc



Dmitry A. Kazakov

unread,
May 22, 2012, 1:03:24 PM5/22/12
to
On Tue, 22 May 2012 17:25:17 +0100, BartC wrote:

> "Dmitry A. Kazakov" <mai...@dmitry-kazakov.de> wrote in message
> news:15puwddz4h6cl$.34i9lxveafeb.dlg@40tude.net...
>> On Tue, 22 May 2012 10:55:01 +0100, BartC wrote:
>>
>>> With the new-unsigned type behaving as I have suggested a few times [in the
>>> original thread in comp.lang.c], just a subrange of a signed type (for
>>> example, a hypothetical signed integer type that is one bit wider).
>>>
>>> With the new-unsigned type, a negation operation must have signed result
>>> (because, other than -0, it will be negative). While subtraction would need
>>> to be signed too, as results can be negative. And of course there is the
>>> likelihood of overflow or underflow, for which I will leave to other
>>> language proposals to deal with..
>>
>> Integer arithmetic is more than just + and -. Did you ask yourself what is
>> the motivation to have it one bit wider?
>
> Yes. So that given 32 bits for example, we can count to approximately 4
> billion instead of 2 billion.
>
>> The answer is to have + and -
>> closed. What about *? That would make it x2 bits wider. What about
>> exponentiation? Much more bits. Factorial?
>
> We're talking about C where operations and their results usually have the
> same predefined width.

Irrelevant. The concept either works or does not. In this case it does not.

BartC

unread,
May 22, 2012, 1:26:58 PM5/22/12
to
"Dmitry A. Kazakov" <mai...@dmitry-kazakov.de> wrote in message
news:3b5cewxtwayd.x...@40tude.net...
> On Tue, 22 May 2012 17:25:17 +0100, BartC wrote:
>> "Dmitry A. Kazakov" <mai...@dmitry-kazakov.de> wrote in message

>>> The answer is to have + and -
>>> closed. What about *? That would make it x2 bits wider. What about
>>> exponentiation? Much more bits. Factorial?
>>
>> We're talking about C where operations and their results usually have the
>> same predefined width.
>
> Irrelevant. The concept either works or does not. In this case it does
> not.

That's fine. But in that case, every calculator or computer ever made is
useless, because you can always think up some calculation just beyond it's
capacity.

--
Bartc

Martin Shobe

unread,
May 22, 2012, 1:30:09 PM5/22/12
to
gwowen wrote:

> On May 20, 11:37�pm, James Kuyper <jameskuy...@verizon.net> wrote:
>
>> Note: "mathematically wrong" would require identification of specific
>> aspects of math that are supposed to correspond to C operators. Oddly
>> enough, mathematics is sufficiently broad field of study that it does
>> encompass concepts that correspond very well with the behavior required
>> by the C standard; they're just not the concepts you think should be
>> relevant. The problem is not that the choices made by the C standard are
>> mathematically wrong, it's only that they're different from the ones you
>> think they should have made.
>
> While there's a lot of truth to that, the problem is that the C
> language defines operators on those abstractions that no mathematician
> would.
>
> For arithmetic (+,-,* and [maybe] /) the unsigned integers model Z_N
> for some large value of N. That's all fine and dandy, but no sane
> mathematician attempts to consider Z_N to be an ordered field, because
> there aren't any total orderings that behave sanely w.r.t. addition or
> multiplication.

That, and for the Ns in question (powers of 2), Z_N isn't a field.

>
> So the unsigned integers aren't just "not the mathematical concept we
> think", they're a solidly defined mathematical concept, with a load of
> horribly inappropriate concepts bolted on, whose behaviour is far more
> closely related to how silicon works, rather than how mathematics
> works.

Except that there is a mathematical object that corresponds exactly to
the defined behaviour (including <), namely Z_N with the ordered induced
by the natural order of N. Yes, it's not as "nice" as an ordered field,
but it's still there.

>
> So as soon as we use comparison operators, the "its just a closed
> group" argument falls utterly to pieces.

Since the definition of group doesn't restrict the ordering in any way,
nothing at all happens to the "it's just a closed group" argument.

Martin Shobe

Fritz Wuehler

unread,
May 22, 2012, 1:56:13 PM5/22/12
to
"BartC" <b...@freeuk.com> wrote:

> "Nomen Nescio" <nob...@dizum.com> wrote in message
> news:eb42d1e8b4d1e87b...@dizum.com...
> > glen herrmannsfeldt <g...@ugcs.caltech.edu> wrote:
>
> >> I don't know how ADA treats signed values, or how its bitwise
> >> operators work. This is still posted to the ADA group, though.
> >
> > I'm not an expert on this but Ada is very strongly (statically) typed.
> > There
> > are only two integer types in Ada to start with, signed integer and
> > modular
> > integer. The compiler will flag an error if you try to compare variables
> > of
> > those two types or indeed variables of any differing types.
>
> Signed and modular is a better way of explaining how C treats these types,
> with a clear distinction between them.

That's only partially true from an Ada perspective. For one thing, Ada
allows you to specify a range for modular types (and for integer types as
well) so you (and the compiler) actually know what modulo you are using. In
C, if you assign a negative or invalid value to an unsigned int I believe
the result you get depends on the C implementation and execution platform.
In Ada, the result should be consistent across all implementations and
platforms.

> Signed types clearly should be the primary way to represent actual integer
> quantities as most of us think of them. A signed integer range can represent
> any positive value (given enough bits); an unsigned range can't represent
> any positive or negative value (not with a finite number of bits anyway).

I don't agree with this because in many practical cases unsigned integers
are more meaningful (counters, for example) and also extend the useful range
of machines with small word sizes. If really depends on your application.

> That's why it's odd that when there is any hint of a unsigned operand, C
> should switch to modular arithmetic for both.

I agree. It's the opposite of what you would expect. It seems to me unsigned
integers should probably be promoted to signed integer to be the most useful
but of course you will also need some way to handle overflows.

Dmitry A. Kazakov

unread,
May 22, 2012, 2:18:08 PM5/22/12
to
On Tue, 22 May 2012 18:26:58 +0100, BartC wrote:

> "Dmitry A. Kazakov" <mai...@dmitry-kazakov.de> wrote in message
> news:3b5cewxtwayd.x...@40tude.net...
>> On Tue, 22 May 2012 17:25:17 +0100, BartC wrote:
>>> "Dmitry A. Kazakov" <mai...@dmitry-kazakov.de> wrote in message
>
>>>> The answer is to have + and -
>>>> closed. What about *? That would make it x2 bits wider. What about
>>>> exponentiation? Much more bits. Factorial?
>>>
>>> We're talking about C where operations and their results usually have the
>>> same predefined width.
>>
>> Irrelevant. The concept either works or does not. In this case it does
>> not.
>
> That's fine. But in that case, every calculator or computer ever made is
> useless, because you can always think up some calculation just beyond it's
> capacity.

But calculators do not work with non-modular unsigned integers.

The idea of having a constrained subtype of a constrained or not integer
type is all OK. This is how such types are defined in Ada:

subtype Natural is new Integer range 0..Integer'Last;

Wrong is an attempt to define a new type pretending it were unconstrained
because some extra bits.

Tim Rentsch

unread,
May 22, 2012, 5:02:42 PM5/22/12
to
I just downloaded a Digital Mars C compiler, and compiled
this program (held in file n.c):

#include <stdio.h>

int
main(){
printf( "Hello, world!\n" );
printf( "-1 < sizeof 1 == %d\n", -1 < sizeof 1 );
printf( "-1 < 0l + sizeof 1 == %d\n", -1 < 0l + sizeof 1 );
printf( "-1 < 0ll + sizeof 1 == %d\n", -1 < 0ll + sizeof 1 );
return 0;
}

which, when run, produced

Hello, world!
-1 < sizeof 1 == 0
-1 < 0l + sizeof 1 == 0
-1 < 0ll + sizeof 1 == 1

for its output. This was dmc852, IIRC.

BartC

unread,
May 22, 2012, 5:49:02 PM5/22/12
to
"Tim Rentsch" <t...@alumni.caltech.edu> wrote in message
news:kfnaa10...@x-alumni2.alumni.caltech.edu...
> "BartC" <b...@freeuk.com> writes:

>> I didn't see anything obvious amongst all the options. And I made sure
>> it was the latest version.
>
> I just downloaded a Digital Mars C compiler, and compiled
> this program (held in file n.c):
>
> #include <stdio.h>
>
> int
> main(){
> printf( "Hello, world!\n" );
> printf( "-1 < sizeof 1 == %d\n", -1 < sizeof 1 );
> printf( "-1 < 0l + sizeof 1 == %d\n", -1 < 0l + sizeof 1 );
> printf( "-1 < 0ll + sizeof 1 == %d\n", -1 < 0ll + sizeof 1 );
> return 0;
> }
>
> which, when run, produced
>
> Hello, world!
> -1 < sizeof 1 == 0
> -1 < 0l + sizeof 1 == 0
> -1 < 0ll + sizeof 1 == 1
>
> for its output. This was dmc852, IIRC.

Mine said 8.42n. But if you used the -v2 verbose switch, it said 8.52.5n.

Your code also gave 0,0,1 on my DMC, and on two other C compilers. But 0,0,0
on lcc-win32.

I extended the program to do sizeof on strings again (and add some
parentheses):

printf( "-1 < sizeof 1 == %d\n", -1 < sizeof 1 );
printf( "-1 < (0l + sizeof 1) == %d\n", -1 < (0l + sizeof 1) );
printf( "-1 < (0ll + sizeof 1) == %d\n", -1 < (0ll + sizeof 1) );
printf( "-1 < sizeof \"\" == %d\n", -1 < sizeof "" );
printf( "-1 < (0l + sizeof \"\") == %d\n", -1 < (0l + sizeof "") );
printf( "-1 < (0ll + sizeof \"\") == %d\n", -1 < (0ll + sizeof "") );

This gave these results:

lccwin32: 0,0,0, 0,0,0
DMC: 0,0,1, 1,1,1
gcc: 0,0,1, 0,0,1
PellesC: 0,0,1, 0,0,1

So definitely something funny with DMC when using sizeof on string literals,
up to 32-bits anyway.

One thing I found puzzling, was why the long-long version compared
differently. Is seemed to give the wrong result (for what C purports to do)
on every compiler except lcc-win32.

But on the whole, I'm more confused now than before..

--
Bartc

James Kuyper

unread,
May 22, 2012, 6:17:35 PM5/22/12
to
On 05/22/2012 05:49 PM, BartC wrote:
...
> I extended the program to do sizeof on strings again (and add some
> parentheses):
>
> printf( "-1 < sizeof 1 == %d\n", -1 < sizeof 1 );
> printf( "-1 < (0l + sizeof 1) == %d\n", -1 < (0l + sizeof 1) );
> printf( "-1 < (0ll + sizeof 1) == %d\n", -1 < (0ll + sizeof 1) );
> printf( "-1 < sizeof \"\" == %d\n", -1 < sizeof "" );
> printf( "-1 < (0l + sizeof \"\") == %d\n", -1 < (0l + sizeof "") );
> printf( "-1 < (0ll + sizeof \"\") == %d\n", -1 < (0ll + sizeof "") );
>
> This gave these results:
>
> lccwin32: 0,0,0, 0,0,0
> DMC: 0,0,1, 1,1,1
> gcc: 0,0,1, 0,0,1
> PellesC: 0,0,1, 0,0,1
>
> So definitely something funny with DMC when using sizeof on string literals,
> up to 32-bits anyway.
>
> One thing I found puzzling, was why the long-long version compared
> differently. Is seemed to give the wrong result (for what C purports to do)
> on every compiler except lcc-win32.

Check the values of SIZE_MAX and LLONG_MAX. If SIZE_MAX < LLONG_MAX, the
usual arithmetic conversions result in the sizeof expression and -1 both
being converted to long long, so a result of 1 is to be expected.
Perhaps lccwin32 is the only one of those compilers for which SIZE_MAX
>= LLONG_MAX?

The result you're getting with DMC are non-conforming; sizeof 1 and
sizeof "" both have the type size_t, and a non-negative value, so you
should be getting the same result with either expression, whether that
value is 0 or 1.

Terence

unread,
May 22, 2012, 7:19:18 PM5/22/12
to
Les:
See UK TV program "Geordy shore". I'm a northerner myself. The post-1945
period brought back voluble armed forces personnel and I first saw these
obsenities enter the spoken language (especially in the military area; I
also met this as a cadet sargeant, 1948-53). It's there; I'm ashamed for
usage on international programs fo rthe impresion it gives.


Les Neilson

unread,
May 23, 2012, 3:20:17 AM5/23/12
to

"Terence" <tbwr...@bigpond.net.au> wrote in message
news:z9Vur.7164$v14....@viwinnwfe02.internal.bigpond.com...
You are probably right.
I have never watched "Geordy shore" (I'm not a fan of so-called "reality
tv"). I guess I'm the one who has led a sheltered life :-)
Les

Terence

unread,
May 23, 2012, 3:26:41 AM5/23/12
to
Funny; I remember the time when a "calculator' meant a person who calculated
for a living.
The clockwork machine he/she might very well use only understand a negative
input as rotation of the crank one way, and positive as going the other
over-the-top way.
Would you believe I did a postgraduate degree in how, precisely, to use one
of those hand-crank things properly, when studying Numerical Analysis? Yes,
we had electricity of a sort (D.C.at 240 volt; no coil-based transformers -
I had a minature train 00 layout with a dynamo-generator to get 240v down to
12v DC), but see R.Feynman on his opinion of the use of powered omputing
devices at Los Alamos.
It was very much later I met my first American Marchant machine (after we we
got A.C. power).
How could the electical power engineers guess wrong ?(like Edison did in the
USA actually).



gwowen

unread,
May 23, 2012, 4:02:27 AM5/23/12
to
Well, yes, but an ordering on a field that does not respect the field
operations (or even the group operation) has no uses. You have a
group, and you have an order, but *you don't have an ordered group*.
You have something so useless, it doesn't even have a name. It's not
an ordering in any meaningful way. It's a novelty, not an abstract
model of anything useful. No-one studies them.

And, since Z_N is often defined in terms of equivalence classes (x ~ x
+N), what you say is not even true.

Martin Shobe

unread,
May 24, 2012, 9:29:32 AM5/24/12
to
Oh please, it quite obviously has at least one use.

> You have a
> group, and you have an order, but *you don't have an ordered group*.

No one claimed it was an ordered group. But why should we restrict
ourselves to such?

> You have something so useless, it doesn't even have a name. It's not
> an ordering in any meaningful way. It's a novelty, not an abstract
> model of anything useful. No-one studies them.

There aren't enough names to give everything in mathematics a name. So
why should that matter? In any case, it does have a name, you've used
it. It also belongs to several classes of structures including
commutative associative rings with units, groups, etc. which are study,
and when they get studied, so does it.

As for it studying that particular combination of group properties and
ordering? Well, they do study it, they just study each aspect seperately
since the two don't interact in any meaningful way.

> And, since Z_N is often defined in terms of equivalence classes (x ~ x
> +N), what you say is not even true.

With that definition of Z_N, you'd define < using the smallest
member of each equivalence class.

Martin Shobe

Tim Rentsch

unread,
May 31, 2012, 7:04:25 PM5/31/12
to
I played around with dmc852 a little more, and found out the
following:

1. The type 'size_t' is the same as 'unsigned int'.
Note that signed/unsigned long have the same
sizes as signed/unsigned int, even though they
are different types, and signed/unsigned long long
is larger. This explains the '0,0,1' behavior
for the different comparisons (ie, no addition,
adding 0L, and adding 0LL). So that's all by
the book.

2. Apparently the particular case for sizeof applied
to a string literal has a type of int rather than
size_t (which is the same as unsigned int). That's
why '-1 < sizeof "foo"' produces the result it
does. Needless to say, taking int as the type for
a sizeof expression, even just the one case of
sizeof applied to a string literal, is not allowed
in a conforming implementation. This is a bug in
the DM implementation. (I didn't find any other
cases in dmc852 where sizeof gives a signed type
rather than an unsigned type.)

3. Curious observation: although this is nominally a
C99 implementation (it supports variable length
arrays, for example), it does not appear to support
compound literals.
0 new messages