Home > Software design >  Conversion Warning with Bitwise Or and Casted Operands
Conversion Warning with Bitwise Or and Casted Operands

Time:10-22

Code Snippet 1 (shown below) produces the following -Wconversion warning:

debug_Wconversion.c:10:57: warning: conversion to ‘uint16_t’ from ‘int’ may alter its value [-Wconversion]
     result = ((uint16_t) (((uint16_t) byte1) & 0x0050)) | ((uint16_t) byte2);
                                                         ^

Code snippets 2 and 3 do not produce a -Wconversion warning.

Code Snippet 1:

uint8_t  byte1 = 0xF0;
uint8_t  byte2 = 0x0F;
uint16_t result;
result = ((uint16_t) (((uint16_t) byte1) & 0x0050)) | ((uint16_t) byte2);

Code Snippet 2:

uint8_t  byte1 = 0xF0;
uint8_t  byte2 = 0x0F;
uint16_t result;
uint16_t leftOrOperand;
uint16_t rightOrOperand;
leftOrOperand  = ((uint16_t) (((uint16_t) byte1) & 0x0050));
rightOrOperand = ((uint16_t) byte2);
result = leftOrOperand | rightOrOperand;

Code Snippet 3:

uint8_t  byte1 = 0xF0;
uint8_t  byte2 = 0x0F;
uint16_t result;
result = (uint16_t) (((uint16_t) (((uint16_t) byte1) & 0x0050)) | ((uint16_t) byte2));

Why does the first code snippet produce the warning, but the others do not? What specifically is causing the warning?

I find this perplexing. The warning in the first snippet insinuates that integer promotion is occurring in the Bitwise Or operation, despite the explicit casting of the operands. The second snippet demonstrates that the Bitwise Or operation does not inherently cause integer promotion. The third snippet performs the same operation as the first snippet if order of operations is preserved, but casts the result of the Bitwise Or before assignment of the result variable. There is clearly something that I fail to understand about the interplay between casting and the Bitwise Or operation. Any help with clarifying my comprehension would be greatly appreciated!

Other potentially relevant information:

  • gcc (GCC) 4.8.5 20150623 (I am unable to upgrade, but this also seems to occur on newer versions)
  • -std=c11
  • -Wconversion warning is present with default optimization (no supplied setting) and with disabled optimization (-O0)

Thanks in advance!

CodePudding user response:

Both the bitwise OR operator | and the bitwise AND operator & perform integer promotions on both operands. The fact that one of the operands is the result of a cast doesn't change this.

The -Wconversion flag tends to be a bit overenthusiastic regarding what it warns on. Given a 32-bit int, a conversion from uint16_t to int will not change its value.

The C standard does specify that the minimum range of an int is -32767 to 32767, so an implementation with this limit could potentially see a value change, though you'll be hard-pressed to find a system the runs gcc where this is the case.

This particular warning is silenced however by a cast. From the man page:

-Wconversion

Warn for implicit conversions that may alter a value. This includes conversions between real and integer, like "abs (x)" when "x" is "double"; conversions between signed and unsigned, like "unsigned ui = -1"; and conversions to smaller types, like "sqrtf (M_PI)". Do not warn for explicit casts like "abs ((int) x)" and "ui = (unsigned) -1", or if the value is not changed by the conversion like in "abs (2.0)". Warnings about conversions between signed and unsigned integers can be disabled by using -Wno-sign-conversion.

For C , also warn for confusing overload resolution for user-defined conversions; and conversions that never use a type conversion operator: conversions to "void", the same type, a base class or a reference to them. Warnings about conversions between signed and unsigned integers are disabled by default in C unless -Wsign-conversion is explicitly enabled.

CodePudding user response:

Why does the first code snippet produce the warning, but the others do not? What specifically is causing the warning?

((uint16_t) (((uint16_t) byte1) & 0x0050)) | ((uint16_t) byte2) is an int being assigned to a uint16_t.

((uint16_t) (((uint16_t) byte1) & 0x0050)) is a uint16_t.
((uint16_t) byte2) is also a uint16_t.
uint16_t | uint16_t first promotes the operands to int before the |.

Code is assigning an int to a uint16_t - hence the warning.

In the 2nd snippet, the compiler better understands or'ing two uint16_t (result is also int) will not incur a conversion issue down-converting to uint16_t. The first was just a tad too complex for the compiler to deduce no issue.

In the 3rd, the final cast silenced any down conversion issue.


Alternative: Use an unsigned constant: 0x0050u and unsigned math for unsigned types like uint16_t.

uint8_t  byte1 = 0xF0u;
uint8_t  byte2 = 0x0Fu;
//                 v-------------v unsigned result
//                         v-----v unsigned constant
uint16_t result = (byte1 & 0x0050u) | byte2;
//                ^-----------------------^ unsigned result
//       ^--------------------------------^ initializing: unsigned to uint16_t

A pesky compiler may still whine about the narrowing initialization.

// Non-cast alternative
uint16_t result = UINT16_MAX & ((byte1 & 0x0050u) | byte2);
// Cast alternative
uint16_t result = (uint16_t)   ((byte1 & 0x0050u) | byte2);
  • Related