3

I don't understand why compiler (GCC) throws [-Wconversion] warnings at this lines (the code works perfect).

 uint16_t ICACHE_FLASH_ATTR crc(uint8_t data, uint16_t crc)
    {
        int i = 8;

        crc = crc ^ (uint16_t)data << 8; // <-- here

        do
        {
            if (crc & 0x8000)
                crc = crc << 1 ^ 0x1021; // <-- here
            else
                crc = crc << 1; // <-- and here
        } while (--i);

        return crc;
    }

All operands are uint16_t so why complain?

If constants are evaluated to int, how can I get rid of warnings (not disabling them).

I tried also with no luck

crc = crc ^ (uint16_t)data << 8UL;

Thanks,

user2736738
  • 30,591
  • 5
  • 42
  • 56
yo3hcv
  • 1,531
  • 2
  • 17
  • 27
  • The compiler identifies the result of the shift expressions to be `int`. You'd have to cast the result of the expressions to get rid of these warnings (i.e. `(uint16_t)(data << 8)`) but casting integers around isn't always a good idea unless one is quite sure about what is going on. – Pixelchemist Jan 14 '18 at 16:28
  • 1
    This `(uint16_t)data << 8` has `int` value, despite your cast, which was applied before the shift, so it is possible it can be truncated. I suggest `(uint16_t)(data << 8)`. Note that `data` is promoted to `int` before the shift. – Weather Vane Jan 14 '18 at 16:29
  • 2
    Similarly with `crc = crc << 1` where the result of `crc << 1` is of type `int`. Suggest `crc = (uint16_t)(crc << 1)`. – Weather Vane Jan 14 '18 at 16:34
  • @Weather Vane I've just been fooled these days, there are no default promotions :) Perhaps I should enable them but I prefer like this so I can truncate of shift. However, ok, it works, thanks. – yo3hcv Jan 14 '18 at 16:35
  • 3
    @orfruit C11 says *"6.5.7 Bitwise shift operators . . . ¶3 The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand."* – Weather Vane Jan 14 '18 at 16:53
  • I am on ISO C99 (-std=c99), I will check anuhow. – yo3hcv Jan 14 '18 at 17:07
  • @orfruit C99 says the same, with same paragraph number. – Weather Vane Jan 14 '18 at 17:10
  • @WeatherVane What does that mean for `val << 1`, when `val` is a `uint64_t`?. Does `val` also get promoted to `int`? – Pablo Jan 14 '18 at 17:26
  • 2
    @Pablo there's no promotion in that case, if `sizeof(int) <= sizeof(uint64_t)` https://stackoverflow.com/q/46073295/995714 – phuclv Jan 14 '18 at 17:28
  • some have suggested `data << 8` but that would cause UB for some inputs on system with 16-bit `int` – M.M Jan 14 '18 at 21:05

1 Answers1

1

Assuming a machine with sizeof(int) == 4 and CHAR_BITS == 8 (so sizeof(int) > sizeof(uint16_t), the C standard stipulates that:

crc = crc ^ (uint16_t)data << 8;

undergoes the 'usual arithmetic conversions', and is treated more or less as though you'd written (the 8 is already an int so it doesn't get converted):

crc = (uint16_t)( ((int)crc) ^ (((int)((uint16_t)data)) << 8) );

And it is the assignment of the int result of the xor operator in the assignment to the uint16_t variable crc that triggers (accurately) the warning.

Similar remarks apply to each of the two other lines marked.

If you were working on a machine where sizeof(int) == sizeof(uint16_t), you would not be getting the warnings in the first place.

6.3.1.8 Usual arithmetic conversions:

¶1 Many operators that expect operands of arithmetic type cause conversions and yield result types in a similar way. The purpose is to determine a common real type for the operands and result. For the specified operands, each operand is converted, without change of type domain, to a type whose corresponding real type is the common real type. Unless explicitly stated otherwise, the common real type is also the corresponding real type of the result, whose type domain is the type domain of the operands if they are the same, and complex otherwise. This pattern is called the usual arithmetic conversions:

  • …floating point omitted…

  • Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:

    • If both operands have the same type, then no further conversion is needed.

    • Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.

    • Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.

    • Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.

    • Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.

And 6.3.1.1 Boolean, characters, and integers says:

¶2 … 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. These are called the integer promotions.58) All other types are unchanged by the integer promotions.

58) The integer promotions are applied only: as part of the usual arithmetic conversions, to certain argument expressions, to the operands of the unary +, -, and ~ operators, and to both operands of the shift operators, as specified by their respective subclauses.

§6.3.1.1¶1 defines 'rank' in excruciating detail, but basically bigger types have a higher rank than smaller types (so long has a higher rank than signed char).

You can then find the specifications for the various operators:

6.5.7 Bitwise shift operators:

Each of the operands shall have integer type.

The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand.

6.5.11 Bitwise exclusive OR operator:

Each of the operands shall have integer type.

The usual arithmetic conversions are performed on the operands.

6.5.16 Assignment operators

An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after the assignment,111) but is not an lvalue. The type of an assignment expression is the type the left operand would have after lvalue conversion.

111) The implementation is permitted to read the object to determine the value but is not required to, even when the object has volatile-qualified type.

6.5.16.1 Simple assignment:

In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278