6

while writing the code I observe one thing in my code its related to the comparison of bit-field value with negative integers.

I have one structure member of unsigned of size one bit and one unsigned int. When I compare the negative value with unsigned int variable I am getting expected result as 1 but when I compare the structure member with the negative value I am getting the opposite result as 0.

#include <stdio.h>
struct S0
{
   unsigned int bit : 1;
};
struct S0 s;

int main (void) 
{
   int negVal = -3;
   unsigned int p = 123;
   printf ("%d\n", (negVal > p)); /*Result as 1 */
   printf ("%d\n", (negVal > s.bit));/*Result as 0 but expected 1 */
   return 0;
}

My doubt is if I compare the negative value with unsigned int then balancing will happen (implicit type casting). But if I compare structure member of unsigned int why implicit type casting is not happening. Correct me if I miss any basics of bit fields?

goodman
  • 424
  • 8
  • 24

2 Answers2

5

(move my remark as an answer)

gcc promotes s.bit to an int, so (negVal > s.bit) does (-3 > 0) valuing 0

See Should bit-fields less than int in size be the subject of integral promotion? but your question is not a duplicate of it.


(negVal > p) returns 1 because negVal is promoted to unsigned producing a big value, see Signed/unsigned comparisons

bruno
  • 32,421
  • 7
  • 25
  • 37
  • @Vagish — the signed value is convert to (large) unsigned. – Jonathan Leffler Feb 18 '19 at 08:08
  • @bruno—gcc promotes s.bit to an int, but the bit is of type unsigned int and in the draft c++ document it mentioned as "it can be converted to unsigned int if unsigned int can represent all the values of the bit-field", so it should cast to unsigned int right? – goodman Feb 18 '19 at 08:36
  • 1
    @swaraj the moral of the story is not to make any comparison between signed and unsigned, and for that add the necessary cast to indicate the expected conversion – bruno Feb 18 '19 at 08:42
  • @swaraj: The question is tagged C and not C++, so why are you referencing a C++ document? – Eric Postpischil Feb 18 '19 at 13:19
  • @Eric Postpischil - that is the only answer I got for the question, I want answer in c so I didn't accept that just I referred the document – goodman Feb 18 '19 at 14:24
  • @swaraj ahhhh yes you are in C and my links are for C++ – bruno Feb 18 '19 at 14:26
  • @swaraj: C and C++ semantics differ. Using a C++ document to interpret C code can cause errors. In any case, the passage you cite does not indicate that an unsigned bit field is converted to `unsigned int`. It actually says “**otherwise,** it can be converted to `unsigned int`”, and the preceding text says it can be converted to `int` if `int` can represent all the values of the bit field. Since it is a single bit, an `int` can represent all of its values, so it would, if this C++ passage applied, be converted to `int`. The “otherwise” clause about `unsigned int` does not apply. – Eric Postpischil Feb 18 '19 at 14:27
  • @Eric Postpischil: yes Eric, that's my mistake only.Do you have any solution/reference for the above question? – goodman Feb 18 '19 at 14:31
2

For illustration, the following uses a 32-bit int and a 32-bit unsigned int.

In negVal > p:

  • negVal is an int with value −3.
  • p is an unsigned int with value 123.
  • C 2018 6.5.8 3, which is discusses > and the other relational operators, tells us that the usual arithmetic conversions are performed on the operands.
  • 6.3.1.8 1 defines the usual arithmetic conversions. For integer types, the first step of the usual arithmetic conversions is to perform the integer promotions on each operand.
  • 6.3.1.1 2 defines the integer promotions. int, unsigned int, and integer types wider than these are unchanged. For other integer types, it says: ”If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int.”
  • Since negVal is an int, it is unchanged by the integer promotions.
  • Since p is an unsigned int, it is unchanged by the integer promotions.
  • The next step in the usual arithmetic conversions is to convert one operand to the type of the other. For int and unsigned int, the int is converted to unsigned int.
  • Converting the int −3 to unsigned int results in 4,294,967,293. (The conversion is defined to add or subtracting UINT_MAX + 1, which is 4,294,967,296, to the value as many times as necessary to bring it in range. This is equivalent to “wrapping” modulo 4,294,967,296 or to reinterpreting the two’s complement representation of −3 as an unsigned int.)
  • After the conversions, the expression negVal > p has become 4294967293u > 123u.
  • This comparison is true, so the result is 1.

In negVal > s.bit:

  • negVal is an int with value −3.
  • s.bit is a one-bit bit-field with value 0.
  • As above, the usual arithmetic conversions are performed on the operands.
  • As above, the first step of the usual arithmetic conversions is to perform the integer promotions on each operand.
  • Since negVal is an int, it is unchanged by the integer promotions.
  • Since s.bit is a bit-field narrower than an int, it will be converted by the integer promotions. This one-bit bit-field can represent either 0 or 1. Both of these can be represented by an int, and therefore the rule “If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int” applies.
  • Converting 0 to int results in 0.
  • The next step in the usual arithmetic conversions would be to convert one operand to the type of the other. Since both operands are now int, no conversion is needed.
  • After the conversions, the expression negVal > s.bit has become -3 > 0.
  • This comparison is false, so the result is 0.
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • thank you Eric for your clear explanation, out of curiosity I am asking how much bit width is required to typecast s.bit as unsigned integer instead of int because int can hold 32 bits... – goodman Feb 18 '19 at 15:37
  • @swaraj: A *cast** is an operator. It has the form `(type)`. When you write `(unsigned int) −3`, that is a cast, and the operation it performs is a conversion. Conversions are also performed in other places. For example, when you write `a = b`, the value of `b` is converted to the type of `a`. Another example is that the integer promotions perform conversions. You should use “cast” to refer to the operator, and you should use “conversion” to refer to any conversion. – Eric Postpischil Feb 18 '19 at 15:47
  • @swaraj: In order for a bit-field declared with `unsigned int` to be promoted to an `unsigned int` and not be promoted to an `int`, it must have as many bits as an `unsigned int` does. If it has fewer bits, then all the values it can represent can also be represented by an `int`, so it will be promoted to an `int`. The number of bits in an `unsigned int` varies from C implementation to C implementation, so there is no single constant size you can declare a bit-field to be to ensure it is promoted to `unsigned int`. – Eric Postpischil Feb 18 '19 at 15:48
  • @swaraj: To ensure that a bit-field is converted to `unsigned int`, you should use a cast: `negVal > (unsigned int) s.bit`. – Eric Postpischil Feb 18 '19 at 15:48