Bit-fields and integral promotion

J

James Kuyper

Lawrence said:
To be honest it is. Any promotion mechanism that didn't preserve value
would be rather silly. Unsigned preserving promotion also preserves value.
So preserving value is not a distinguishing feature of the standard's
integer promotions.

It's not exactly correct terminology, but there is a connection. Both
kinds of promotions are value preserving in themselves. However, the
"value preserving" rules result in signed types more often than the
"signedness preserving" rules did. As a result, there's less frequent
need for a signed value to be converted to an unsigned type, a
conversion that can't be value preserving the value is negative.
 
L

lawrence.jones

In comp.std.c CBFalconer said:
As you may have gathered from my previous postings, I am deeply
opposed to that. It obviously isn't officially incorporated into
the standard, and doing so would be a mistake.

Of course it's "officially incorporated into the standard", DR 122 is 11
years old and DR 015 that it references is 12 years old! The DRs don't
propose any change to the text of the standard, the implication being
that the committee believes the existing text to be clear enough as is.
There have been
several instances posted in this thread (at least on c.l.c) where
that action leaves the poor programmer in a quandary as to what the
code will do on another machine. One example is the 16 bit
unsigned field, which will be interpreted differently on a 32 bit
and on a 16 bit int machine.

You're just rehashing the argument about unsigned preserving promotion
rules as opposed to value preserving promotion rules. That train left
the station long ago -- the battle was fought and value preserving
promotions won. Bit fields are no different than other "small" types --
having different promotions for them would be sheer lunacy.

-Larry Jones

Any game without push-ups, hits, burns or noogies is a sissy game. -- Calvin
 
L

lawrence.jones

In comp.std.c Tony Finch said:
Er, what? K&R C was signedness-preserving but ANSI C has deliberately
different promotion rules.

Wrong. K&R only had one explicitly unsigned type: unsigned int. As
such, the promotion rules are agnostic. Pre-ANSI compilers that added
additional unsigned types varied between unsigned preserving and value
preserving.

However, it's worth noting that char was implicitly unsigned on some
platforms (notably EBCDIC platforms, due to the requirement that members
of the standard character set have non-negative values). It nonetheless
promoted to int, not unsigned int, the first example of value preserving
behavior.

-Larry Jones

In a minute, you and I are going to settle this out of doors. -- Calvin
 
D

David Hopwood

Alex said:
Granted; "unsigned preserving" is logically a subclass of "value preserving"
but I think it's basically a case of giving a concise name to the behaviour.
There's no immediately obvious and superior alternative that I can think of.

"prefer-signed"?
 
A

Antoine Leca

En (e-mail address removed), Keith Thompson va escriure:
Hmm. A conforming freestanding implementation doesn't have to
provide <stdio.h>, and doesn't have to accept a strictly conforming
program that uses <stdio.h>.

Right. But I am looking from the other side. I have a program which is
claimed to be s.c. ("[The output] must be so with any C compiler"); OTOH it
#includes <stdio.h>. I am arguing that an otherwise compliant freestanding
implementation could provide an (unrequired) <stdio.h> header, so will
compile this program, yet have int of the same width as unsigned char so
will result in the program outputting something different.
If a freestanding implementation does provide
<stdio.h>, I don't see any requirement for it to do so in a manner
that would be conforming for a hosted implementation;

Neither do I. Which is why I challenged the claim.

And on the other hand I cannot spot in the Standard a
requirement for a freestanding implementation to have int strictly
wider from [unsigned] char.

I don't see any such (explicit) requirement even for hosted
implementations.

This might be inferred from the requirement for <ctype.h>:
In all cases the argument is an /int/, the value of which
shall be representable as an /unsigned char/ or shall equal
the value of the macro /EOF/.

I shall not try to convince you on this one since I am feeling there is thin
ice here (and I believe this is as intended, in order to not explicitely
break implementations on computers without "narrow" register access); yet my
reading is that to comply with this, unsigned char should promote to signed
int not unsigned int, and it then comes that int should be "wider".


Antoine
 
L

Lawrence Kirby

For this purpose that is just one more black cat in the dark.

It is a clear example of where the standard requires the width of a
bit-field to be part of its type. Once you accept that is inevitable all
else follows.
The
values are known to be positive, and the standard insists on binary
representation, so masking off a set of less significant bits
produces the same answer for all three flavors. All that has to be
done is make a conversion to some form of unsigned (if needed) in
the original, not in the potential 30 odd or more types pseudo
created by bit fields.

The question is not what could be done or what would be sensible to do,
but what the standard actually requires. And as you seem to be saying it
makes no difference from a code generation point of view.

Lawrence
 
L

Lawrence Kirby

Wrong. K&R only had one explicitly unsigned type: unsigned int. As
such, the promotion rules are agnostic. Pre-ANSI compilers that added
additional unsigned types varied between unsigned preserving and value
preserving.

However, it's worth noting that char was implicitly unsigned on some
platforms (notably EBCDIC platforms, due to the requirement that members
of the standard character set have non-negative values). It nonetheless
promoted to int, not unsigned int, the first example of value preserving
behavior.

I suspected that char was the cause of the whole situation. :) Normally
signed and unsigned integer types are useful for separate problem
domains and mixing them doesn't really make sense. Character handling is
another problem domain again and the desire to promote plain char (which
could be signed or unsigned) to something consistent, with int well
established for character handling, is probably what started C down the
slippery slope.

Lawrence
 
L

Lawrence Kirby

On Wed, 02 Feb 2005 11:06:10 -0500, James Kuyper wrote:

....
It's not exactly correct terminology, but there is a connection. Both
kinds of promotions are value preserving in themselves. However, the
"value preserving" rules result in signed types more often than the
"signedness preserving" rules did. As a result, there's less frequent
need for a signed value to be converted to an unsigned type, a
conversion that can't be value preserving the value is negative.

A little indirect, but OK. :)

Lawrence
 
L

Lawrence Kirby

On Thu, 03 Feb 2005 13:18:50 +0100, Antoine Leca wrote:

....
Neither do I. Which is why I challenged the claim.

Interesting. According to 4p6 A conforming freestanding implementation
isn't required to accept a program that uses <stdio.h>. However 5.1.2.1p1
says

"... Any library facilities available to a freestanding
program, other than the minimal set required by clause 4, are
implementation-defined."

And on the other hand I cannot spot in the Standard a
requirement for a freestanding implementation to have int strictly
wider from [unsigned] char.

I don't see any such (explicit) requirement even for hosted
implementations.

This might be inferred from the requirement for <ctype.h>:
In all cases the argument is an /int/, the value of which shall be
representable as an /unsigned char/ or shall equal the value of the
macro /EOF/.

I shall not try to convince you on this one since I am feeling there is
thin ice here (and I believe this is as intended, in order to not
explicitely break implementations on computers without "narrow" register
access); yet my reading is that to comply with this, unsigned char
should promote to signed int not unsigned int, and it then comes that
int should be "wider".

I don't see a requirement here that every possible value of unsigned char
be passable to the function. Therefore this does not require that
INT_MAX >= UCHAR_MAX.

Lawrence
 
C

CBFalconer

Lawrence said:
On Wed, 02 Feb 2005 11:06:10 -0500, James Kuyper wrote:

...


A little indirect, but OK. :)

Although I don't agree with the philosophy, one attitude could be
that conversion to signed (where possible) averts any future need
to convert unsigned to signed, which is overflow sensitive and thus
not guaranteed possible.
 
L

Lawrence Kirby

In message <[email protected]>


Such things are not subject to integer promotions, according to 6.3.1.1p2.
It doesn't seem terribly concrete, but I think that may mean that a "long:4"
just functions as a long?

Seems to be, it certainly isn't one of the bit-field types specified in
6.3.1.1p2, and there's nothing in 6.3.1.1.p1 to suggest that it has a
lower rank than int.
What about a "unsigned char:4"? Does it act as a
unsigned char, and hence get promoted?

I guess so, it is unclear though. The alternative i.e. having unpromoted
unsigned char values around after the integer promotions could be
problematic.
Ew. Bitfields on larger types are not something I'm familiar with, as our
systems don't support it.

Nevertheless it is something that the standard explicitly allows an
implementation to support.

Lawrence
 
L

Lawrence Kirby

Although I don't agree with the philosophy, one attitude could be
that conversion to signed (where possible) averts any future need
to convert unsigned to signed, which is overflow sensitive and thus
not guaranteed possible.

If it was going to overflow it would end up as unsigned int anyway. If it
was going to overflow after subsequent operations you would have signed
integer overflow if you performed the operations as signed integers. If
you perform suitable checking for overflow it doesn't matter which you
use, although unsigned might be easier.

Lawrence
 
C

CBFalconer

Lawrence said:
If it was going to overflow it would end up as unsigned int anyway. If it
was going to overflow after subsequent operations you would have signed
integer overflow if you performed the operations as signed integers. If
you perform suitable checking for overflow it doesn't matter which you
use, although unsigned might be easier.

I said future, as in further operations on the variable. I think
we all know we can never safely cast an arbitrary unsigned to the
corresponding signed form.
 
C

CBFalconer

Lawrence said:
. snip ...


Nevertheless it is something that the standard explicitly allows
an implementation to support.

So, instead of valiantly defending the status quo it seems it would
be more productive to concentrate on clearing up this whole area,
in such a way as to produce the fewest surprises and the most
clarity. I claim anything that has not made it into the standard
remains an open question.
 
J

Jack Klein

Of course it should enforce a behavior. However that behavior
should be both rational and agree with existing practice. It seems
that existing practice already varies.


Others, and I, have pointed out the anomalies that can result from
this attitude. The opportunity should be taken to clean this up
without contravening any earlier standard. C is already a morass
of insecurities, there is no need to add another.

Personally I would have preferred the choice of "sign preserving"
promotions over the "value preserving" that was selected by the
committee. Apparently there were pre-standard implementations that
promoted unsigned char to signed int, although I can't remember ever
using one in the K&R days.
One ridiculous result of this attitude is that a 1 bit field,
whether declared as int or unsigned int, would always be treated as
-1 or 0. This is contrary to the usual C practice. The
opportunity exists to ensure that booleans stored in bit fields are
properly expanded. Or should we have to write !!x for any boolean
x stored in a bitfield?

The ridiculous thing about the paragraph above is your conclusion, as
I'm sure you'd realize if you rethink it carefully.

A signed int 1 bit field can indeed only contain the values 0 and -1.

An unsigned int 1 bit field can only contain the values 0 and 1. If
it contains the value 1, it will promote to a signed int with the
value 1. How can you possible maintain that it promotes to -1 in a
signed int? Perhaps you are thinking of how you would write assembly
language for some particular processor, but that has nothing to do
with preserving the value.

C conversions, and integral promotions, are always about the value,
not about the bit pattern.

Given the fact that "value preserving" has been the language standard
for a decade and a half, it is far more important that unsigned bit
fields promote exactly the same way is lesser rank integer types of
the same with and range of values.

Assume one of today's most common desk top platform with 8-bit chars,
16-bit shorts, and 32-bit ints. Now consider:

#include <stdio.h>

struct nu
{
unsigned uc: 8;
unsigned us: 16;
};

int main(void)
{
unsigned char uc = 0;
unsigned short us = 0;
struct nu nu = { 0, 0 };
if ((uc - 5) < 0)
puts("unsigned char promotes to signed");
else
puts("unsigned char promotes to unsigned");
if ((nu.uc - 5) < 0)
puts("unsigned :8 promotes to signed");
else
puts("unsigned :8 promotes to unsigned");
if ((us - 5) < 0)
puts("unsigned short promotes to signed");
else
puts("unsigned short promotes to unsigned");
if ((nu.us - 5) < 0)
puts("unsigned :16 promotes to signed");
else
puts("unsigned :16 promotes to unsigned");
return 0;
}

Under your interpretation, the results are different for the 8-bit
unsigned bit-field and the 8-bit unsigned char, and also different for
the 16-bit unsigned bit-field and the 16-bit unsigned short.

Amazingly, I just tested this with four compilers, all of which got it
wrong.

But the standard does specifically say you are wrong:

6.7.2.1 Structure and union specifiers P9:

"A bit-field is interpreted as a signed or unsigned integer type
consisting of the specified number of bits."

So the type of an unsigned int bit field is NOT unsigned int, it is a
(perhaps unnamed type) uintn_t, where 'n' is the width in bits.

And:

6.3.1.1 Boolean, characters, and integers P1

"The rank of a signed integer type shall be greater than the rank of
any signed integer type with less precision."
[snip]
"The rank of any unsigned integer type shall equal the rank of the
corresponding signed integer type, if any."

And finally:

6.3.1.2 P2

"If an int can represent all values of the original type, the value is
converted to an int; otherwise, it is converted to an unsigned int."

The first quote makes it clear that the type of an 'n' bit unsigned
integer bit-field is "n-bit unsigned integer type". By the definition
of all unsigned integer types, it has a precision of n-bits.

If n is less than the number of value bits in a signed int, it is
quite clear that this "n-bit unsigned integer type" has a lesser rank
than type int. This means it is subjected to the "integer
promotions".

And since an int can hold all the values of this type, it must be
promoted to int, not unsigned int.

While these quotations are copied and pasted from a copy of C99, C90
contained the same.

"The opportunity should be taken to clean this up without contravening
any earlier standard." is not possible.
 
C

CBFalconer

Jack said:
.... snip ...

Given the fact that "value preserving" has been the language standard
for a decade and a half, it is far more important that unsigned bit
fields promote exactly the same way is lesser rank integer types of
the same with and range of values.

For types other than bit-fields. However the bit-field language is
ambiguous, and all I am saying is that the opportunity exists to
clean it up to a sensible meaning, and that that sensible meaning
should be unsigned preserving.

Remember, joe q programmer has written "unsigned int" for the type
of the bit field. If he uses the content of that field, he should
expect to get an unsigned int that obeys the laws for unsigned
ints. If he uses that content in some other type, then the value
preserving language of the standard kicks in.

Doing things that way even requires less effort than the misguided
conversion to signed int.
 
T

Tim Rentsch

Jack Klein said:
Assume one of today's most common desk top platform with 8-bit chars,
16-bit shorts, and 32-bit ints. Now consider:

#include <stdio.h>

struct nu
{
unsigned uc: 8;
unsigned us: 16;
};

int main(void)
{
unsigned char uc = 0;
unsigned short us = 0;
struct nu nu = { 0, 0 };
if ((uc - 5) < 0)
puts("unsigned char promotes to signed");
else
puts("unsigned char promotes to unsigned");
if ((nu.uc - 5) < 0)
puts("unsigned :8 promotes to signed");
else
puts("unsigned :8 promotes to unsigned");
if ((us - 5) < 0)
puts("unsigned short promotes to signed");
else
puts("unsigned short promotes to unsigned");
if ((nu.us - 5) < 0)
puts("unsigned :16 promotes to signed");
else
puts("unsigned :16 promotes to unsigned");
return 0;
}

Under your interpretation, the results are different for the 8-bit
unsigned bit-field and the 8-bit unsigned char, and also different for
the 16-bit unsigned bit-field and the 16-bit unsigned short.

Amazingly, I just tested this with four compilers, all of which got it
wrong.

Which ones, and what were the results?
 
K

Keith Thompson

Jack Klein said:
Assume one of today's most common desk top platform with 8-bit chars,
16-bit shorts, and 32-bit ints. Now consider:

#include <stdio.h>

struct nu
{
unsigned uc: 8;
unsigned us: 16;
};

int main(void)
{
unsigned char uc = 0;
unsigned short us = 0;
struct nu nu = { 0, 0 };
if ((uc - 5) < 0)
puts("unsigned char promotes to signed");
else
puts("unsigned char promotes to unsigned");
if ((nu.uc - 5) < 0)
puts("unsigned :8 promotes to signed");
else
puts("unsigned :8 promotes to unsigned");
if ((us - 5) < 0)
puts("unsigned short promotes to signed");
else
puts("unsigned short promotes to unsigned");
if ((nu.us - 5) < 0)
puts("unsigned :16 promotes to signed");
else
puts("unsigned :16 promotes to unsigned");
return 0;
}

Under your interpretation, the results are different for the 8-bit
unsigned bit-field and the 8-bit unsigned char, and also different for
the 16-bit unsigned bit-field and the 16-bit unsigned short.

Amazingly, I just tested this with four compilers, all of which got it
wrong.

Interesting. I just tried it with a number of compilers, and they all
got it right (everything promotes to signed).
 
Y

ytrama

Hi,
I am wondering that in GCC bith cases are "Foo". If I remove bit
fileds in structure and executes, it gives "Bar". Can any one helps me
in explaning why unsigned type is not considering in presence of bit
fileds?.

Thanks,
YTR
 
F

Francis Glassborow

CBFalconer said:
Remember, joe q programmer has written "unsigned int" for the type
of the bit field. If he uses the content of that field, he should
expect to get an unsigned int that obeys the laws for unsigned
ints. If he uses that content in some other type, then the value
preserving language of the standard kicks in.

What really concerns me is that a programmer who has carefully used
unsigned int to avoid undefined behaviour in case of overflow gets his
design overruled by the language.

Coupling the promotion rules with the different behaviour on overflow is
a really poor language design. Without that difference (in the
consequences of overflow) I would not have any strong feelings about the
promotion rules, but as they are we get to a ludicrous position that UB
can be implementation dependent.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,240
Members
46,829
Latest member
KimberAlli

Latest Threads

Top