0

Following situation:

I have macros for storing the complement of a variable together with its original value within a structure. With another macro I want to check if the original value is equal to the complement of the stored complement value. But strangely I do not get the results I may expect. Simplifying the operation, this results in following situation:

#include <stdbool.h>
#include <stdint.h>
#define CHECKVAR(var__, compl__) ((var__^compl__)==-1);

int main()
{
uint8_t var;
uint8_t complementvar;

var=3;
complementvar=~var;

bool checkOK=CHECKVAR(var,complementvar); /*-> I expect that all bits are set and hereby it is equal to "-1", but instead i get "false" */

return 0;
}

My expectation would be (e.g. on a 32bit system :

  1. after the complement operation complementvar has the value 0xfffffffc (after integer promotion caused by the ~ operator to int and assigning the lvalue to complementvar by implicitly casting it down to uint8).
  2. now to the checking macro: the bitwise operation leads to an integer promotion on both sides?:
    • 2a). var: 0000 0000 0000 0000 0000 0000 0000 0011; complementvar: 1111 1111 1111 1111 1111 1111 1111 1100
    • 2b)\ now XOR-ing both var and complementvar shoudl result in 1111 1111 1111 1111 1111 1111 1111 1111
  3. checking the results of the XOR-operation together with -1 (represented as an integer) 1111 1111 1111 1111 1111 1111 1111 1111==1111 1111 1111 1111 1111 1111 1111 1111 shall result in true, but instead i always receive false, because the result of XOR-operation (strangely for me) is 0x000000ff?

What compiler am I using? It's the MSVC++11, but actually the solution should be as compiler independent as possible. As I have to be portable too, would the results be different with a C compiler?

mbed_dev
  • 1,450
  • 16
  • 33
  • I would suggest you reading this: https://stackoverflow.com/questions/10820340/the-need-for-parentheses-in-macros-in-c. Even this is probably not the problem here. – kocica Jul 30 '18 at 12:56
  • @Scheff "*although `uint8_t` is promoted to `unsigned`*" <- not sure about C++, but in C, `uint8_t` is promoted to `int` (as `int` can always represent the whole value range of `uint8_t`). –  Jul 30 '18 at 13:12
  • The operation between two `uint8_t`s may convert/promote them to `int` but without sign extension. ([cppreference:](https://en.cppreference.com/w/cpp/language/implicit_conversion#Integral_promotion) _unsigned char or unsigned short can be converted to int if it can hold its entire value range, and unsigned int otherwise;_) So, the result of `uint8_t` ^ `uint8_t` is `0xff` even if type of `0xff` is `(int)` and unequal to `-1` which would be at least `0xffff` (in case that `sizeof (int)` is only 2). – Scheff's Cat Jul 30 '18 at 13:20
  • ok, now i got it. the integer promotion does not cause a sign-extension. so the solution (in case i want to use the XOR operation in favor of avoiding an explicit cast to the "base type" as I have written in @FelixPalmen s answer) would be to check against `UINT8_MAX` instead against `-1`? – mbed_dev Jul 30 '18 at 13:38
  • @mbed_dev then you're again tied to `uint8_t` of course. –  Jul 30 '18 at 13:40
  • @FelixPalmen I think there is no clean solution here. A "generic" macro for checking it would be by storing the complement value always to the word-size. In my case `uint32_t` instead of `int` shall be also ok. – mbed_dev Jul 30 '18 at 13:45
  • @mbed_dev of course, you can use *type-generic selection* ... –  Jul 30 '18 at 13:46

1 Answers1

3

after the complement operation complementvar has the value 0xfffffffc

No. Look at that code:

uint8_t complementvar;
complementvar=~var;

~var will evaluate to the value you expect, but complementvar has type uint8_t, so the conversion leads to just 0xfc.

In your checking step, this is promoted again to int, but as uint8_t is an unsigned type, no sign extension happens here, you just get 0 bits added. This explains the result you get from xoring (^).

  • actually the solution for your version would be `#define CHECKVAR(var, compl) (((uint8_t)~var) == (compl))`?, because according to `gcc` and `MSVC++11` the result of the complement operator on `compl` is `0xffffff00` as the comüplement operator leads to the same result as what a sign-extension would do – mbed_dev Jul 30 '18 at 13:34
  • 1
    @mbed_dev you're correct, dammit ... I'll just roll back my edit. I assumed you want your macro to be type agnostic, and I **thought** this was the (somewhat clever) way. It isn't :o –  Jul 30 '18 at 13:38