Topic: bit field enum signed/unsigned


Author: "Christopher M. Gurnee" <gurnec_at_mediaone_dot_net@127.0.0.1>
Date: 1999/01/27
Raw View
Bill Gibbons wrote in message ...
>
>Yet another compiler bug.
>
>Variables of an enumeration type are not considered signed or unsigned;
they
>hold one of the values they are declared to hold (expanded to the next
power
>of two, as specified in the standard).

But the *underlying type* of an enumeration must be either signed or
unsigned, as defined by the implementation.

>Since a two-bit bitfield is sufficient to hold the enumeration values
>{0,1,2} (and 3, see below), the value of "bfe.efld" above must be 2.

If the underlying type happens to be signed, a two-bit bitfield would not be
sufficient.  As far as I can tell, nothing in the standard prevents this
possiblilty (did I miss something?)

>From section 7.2, paragraph 6 of the standard:
>
> For an enumeration where emin is the smallest enumerator and emax is the
> largest, the values of the enumeration are the values of the underlying
> type in the range bmin to bmax, where bmin and bmax are, respectively, the
> smallest and largest values of the smallest bit-field that can store emin
> and emax.

Here, the "smallest bit-field that can store emin and emax" could be greater
than two if the underlying type of the enum is signed.  IOW, that last
paragraph does not state anywhere that the smallest bit-field must be two in
this situation.

>      [Footnote: On a two's-complement machine, bmax is the smallest value
>       greater than or equal to max ( abs ( emin ) - 1, abs( emax )) of
>       the form 2M - 1; bmin is zero if emin is non-negative and -( bmax +
1 )
>       otherwise. --- end foonote]

This footnote is not normative.  I think it might be wrong.  It says that
"bmin is zero if emin is non-negative and ... otherwise."  I think it should
read "bmin is zero if the underlying type is unsigned and ... otherwise."

And from section 9.6 paragraph 4:
>
> If the value of an enumerator is stored into a bit-field of the same
> enumeration type and the number of bits in the bit-field is large enough
to
> hold all the values of that enumeration type, the original enumerator
value
> and the value of the bit-field shall compare equal.
>
>Since there are no negative enumerator values in "E", the values of "E" can
>be stored in two bits.  In practice the implementation must treat the
>representation as unsigned, but this is an implementation detail only.

I think that your argument is dependant on the informational footnote.
Perhaps I am misinterpretting something, though.  What do you think?

-Chris



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: bill@gibbons.org (Bill Gibbons)
Date: 1999/01/28
Raw View
In article <EAKr2.3662$Wm.3595622@brnws01.ne.mediaone.net>, "Christopher
M. Gurnee" <gurnec_at_mediaone_dot_net@127.0.0.1> wrote:

> >Variables of an enumeration type are not considered signed or unsigned;
> they
> >hold one of the values they are declared to hold (expanded to the next
> power
> >of two, as specified in the standard).
>
> But the *underlying type* of an enumeration must be either signed or
> unsigned, as defined by the implementation.

The "underlying type" does not specify the implementation.  As one of the
authors of this section, I'm quite certain that the original example
(storing and recovering {0,1,2} in a 2-bit field) is supposed to work
correctly, without any surprising (e.g. negative) results.

-- Bill Gibbons
   bill@gibbons.org


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW@my-dejanews.com
Date: 1999/01/30
Raw View
> In article <7893aa$bju$1@winter.news.rcn.net>, "David"
> <nospam@NOatl-SPAMintl.com> wrote:
>
> > Consider the following (much simplified version of actual code):
> > typedef enum E { Zero=0, One=1, Two=2 };
> > struct BFE
> > {
> >    bool a  : 1 ; bool b : 1 ; ... bool j : 1 ;
> >    E  efld : 2 ;
> > } bfe ;
> > In the case of a the bit field enum above, one might reasonably expect that
> > if the efld value is "Two,"  and the following were written:
> >    int val = bfe.efld ;
> >    cout << val << endl ;
> > that the output would be 2.
> >
> > Instead, the output is -2, i.e. the 2-bit field is being interpreted as a
> > signed field.  Much more importantly, with the following code:
> >
> >    bfe.efld  = Two ;
> >    if( bfe.efld == Two )
> >       cout << "True" << endl
> >    else
> >       cout << "False" << endl ;
> >
> > False is output, rather than true, i.e. the internal use seems inconsistent.
> >
> > The question I have is whether this behavior is allowed by the language or
> > is simply yet another MSC compiler bug.

In article <bill-2201990932340001@bgibbons.vip.best.com>,
  bill@gibbons.org (Bill Gibbons) wrote:
> Yet another compiler bug.

It's allowed by the language. The C++ compiler chose to use a
signed integer for the enumeration type; however, your class
is using a bitfield to contain the enumeration. The bitfield
is too small to hold the largest enumeration value, because +2
requires 3 bits including the sign bit.

> Variables of an enumeration type are not considered signed or unsigned; they
> hold one of the values they are declared to hold (expanded to the next power
> of two, as specified in the standard).

Citation, please?

> Since a two-bit bitfield is sufficient to hold the enumeration values
> {0,1,2} (and 3, see below), the value of "bfe.efld" above must be 2.
>
> From section 7.2, paragraph 6 of the standard:
>
>  For an enumeration where emin is the smallest enumerator and emax is the
>  largest, the values of the enumeration are the values of the underlying
>  type in the range bmin to bmax, where bmin and bmax are, respectively, the
>  smallest and largest values of the smallest bit-field that can store emin
>  and emax.
>
>       [Footnote: On a two's-complement machine, bmax is the smallest value
>        greater than or equal to max ( abs ( emin ) - 1, abs( emax )) of
>        the form 2M - 1; bmin is zero if emin is non-negative and -( bmax + 1 )
>        otherwise. --- end foonote]

Wrong paragraph. Look at p5:

  5 The underlying type of an enumeration is an integral type that
    can represent all the enumerator values defined in the enumeration.
    It is implementation-defined which integral type is used as the
    underlying type for an enumeration except that the underlying type
    shall not be larger than int unless the value of an enumerator
    cannot fit in an int or unsigned int. ...

> And from section 9.6 paragraph 4:
>
>  If the value of an enumerator is stored into a bit-field of the same
>  enumeration type and the number of bits in the bit-field is large enough to
>  hold all the values of that enumeration type, the original enumerator value
>  and the value of the bit-field shall compare equal.
>
> Since there are no negative enumerator values in "E", the values of "E" can
> be stored in two bits.  In practice the implementation must treat the
> representation as unsigned, but this is an implementation detail only.

Since the implementation decided to use signed int as the enumeration
type, three bits are required to hold the largest possible value.

Solution 1: Expand the bitfield to 3 bits, large enough for the
largest enumeration value including the sign bit.

Solution 2: Since WE know that the values will always be
non-negative, we can teach the compiler how to access the
field:

    struct BFE
    {
        bool a  : 1 ; bool b : 1 ; ... bool j : 1 ;
        unsigned int e_fld : 2 ;
        E efld() { return E(e_fld); }
        efld(E e) { e_fld = e; }
    } bfe ;

After appropriate syntax changes, both of your examples now work
correctly.

{ MSVC gives you a way to make these same changes transparently,
  without any syntax changes. A "property" is a non-static
  "virtual data member"; the compiler changes their references
  into function calls, such as the efld functions above.
  This is very very non-standard, and as such need not be discussed
  further in this newsgroup; if you're interested in using this
  non-standard feature, consult help for "declspec(property)". ]

----
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: bill@gibbons.org (Bill Gibbons)
Date: 1999/01/31
Raw View
In article <78t1mg$1ot$1@nnrp1.dejanews.com>, AllanW@my-dejanews.com wrote:

> Since the implementation decided to use signed int as the enumeration
> type, three bits are required to hold the largest possible value.

One more time:  enumerations are not integral types.  Only integral types
are signed or unsigned.  The implementation may make the *underlying* type
signed, but the underlying type is *not* the implementation type.
(Nowhere does the standard say that "underlying" means "implementation",
people have just been incorrectly inferring it.)

The implementation is *not* free to implement a minimal-sized bitfield
for an enumeration with no negative values as signed.  This is described
in 9.6 paragraph 4:

  If the value of an enumerator is stored into a bit-field of the same
  enumeration type and the number of bits in the bit-field is large enough
  to hold all the values of that enumeration type, the original enumerator
  value and the value of the bit-field shall compare equal.

This paragraph does not mention signedness.  Two bits is indeed enough
to hold the values {0,1,2}.  An implementation is not free to say
"two bits is not enough because we decided to use a signed representation"
any more than it can say "two bits is not enough because we decided to
represent bitfields using floating-point".

(The implementation might make the underlying type signed, but that's
totally unrelated to the issue.  The "underlying" type is for things
like "sizeof".)

It is unfortunate that this is not made more clear in the standard.  But the
intent was, and still is, as I have described.


-- Bill Gibbons
   bill@gibbons.org
   (X3)J16 committee member
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "David" <nospam@NOatl-SPAMintl.com>
Date: 1999/01/22
Raw View
Consider the following (much simplified version of actual code):
typedef enum E { Zero=0, One=1, Two=2 };
struct BFE
{
   bool a  : 1 ; bool b : 1 ; ... bool j : 1 ;
   E  efld : 2 ;
} bfe ;
In the case of a the bit field enum above, one might reasonably expect that
if the efld value is "Two,"  and the following were written:
   int val = bfe.efld ;
   cout << val << endl ;
that the output would be 2.

Instead, the output is -2, i.e. the 2-bit field is being interpreted as a
signed field.  Much more importantly, with the following code:

   bfe.efld  = Two ;
   if( bfe.efld == Two )
      cout << "True" << endl
   else
      cout << "False" << endl ;

False is output, rather than true, i.e. the internal use seems inconsistent.

The question I have is whether this behavior is allowed by the language or
is simply yet another MSC compiler bug.

   Thanks,
   David
   cpp    @
   atl-intl.com
For spam reduction reasons, my e-mail address is not correct in the header
and white space is inserted above.




[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: bill@gibbons.org (Bill Gibbons)
Date: 1999/01/22
Raw View
In article <7893aa$bju$1@winter.news.rcn.net>, "David"
<nospam@NOatl-SPAMintl.com> wrote:

> Consider the following (much simplified version of actual code):
> typedef enum E { Zero=0, One=1, Two=2 };
> struct BFE
> {
>    bool a  : 1 ; bool b : 1 ; ... bool j : 1 ;
>    E  efld : 2 ;
> } bfe ;
> In the case of a the bit field enum above, one might reasonably expect that
> if the efld value is "Two,"  and the following were written:
>    int val = bfe.efld ;
>    cout << val << endl ;
> that the output would be 2.
>
> Instead, the output is -2, i.e. the 2-bit field is being interpreted as a
> signed field.  Much more importantly, with the following code:
>
>    bfe.efld  = Two ;
>    if( bfe.efld == Two )
>       cout << "True" << endl
>    else
>       cout << "False" << endl ;
>
> False is output, rather than true, i.e. the internal use seems inconsistent.
>
> The question I have is whether this behavior is allowed by the language or
> is simply yet another MSC compiler bug.

Yet another compiler bug.

Variables of an enumeration type are not considered signed or unsigned; they
hold one of the values they are declared to hold (expanded to the next power
of two, as specified in the standard).

Since a two-bit bitfield is sufficient to hold the enumeration values
{0,1,2} (and 3, see below), the value of "bfe.efld" above must be 2.