无符号算术运算中的环绕

Wraparound in unsigned arithmetic operation

在分析以下代码时出现了以下 MISRA 违规行为:

#define z  (USHORT)14745

#define Convert(x) ((((USHORT)x*(unsigned short)1000) + ((z) / (USHORT)2)) /(z))

static const USHORT array [2] = 

{
   Convert(176), -> Line "1"
   Convert(206)  -> Line "2"
};

在“1”、“2”两行上都检测到以下 MISRA 违规:

我的问题是:为什么这个操作会有回绕?!

注意:当我使用调试器检查 array 的值时:

array [2] = 
{
  12,
  14
} 

哪些是正确的值。

首先,176 * 1000 不适合 16 位无符号短整型。因此,通过使用 MISRA,您可以防止代码中出现严重错误,因为该算法是根据带符号的 int 类型计算的,并且其结果隐式显示回无符号短整型。如果得到预期的结果,完全靠coincidence/luck.

请注意,还有另外两个 MISRA 违规未报告:

  • 不允许使用类似函数的宏(规则 19.7)
  • 您应该使用一组预定义的整数类型定义,例如 stint.h(规则 6.3)

这两个都是很好的规则,你不应该忽视。 (它还应该警告您使用不带 'u' 后缀的文字。)

修复方法是用类型安全函数替换混乱的宏,该函数不包含隐式提升(给定 32 位 int):

uint16_t convert (uint16_t x)
{
  const uint16_t z = 14745u;
  uint32_t result32;

  result32 = (uint32_t)x * 1000ul + (uint32_t)z / 2ul / (uint32_t)z
  return (uint16_t)result32;
}

积分推广相关:

Integer types smaller than int are promoted when an operation is performed on them. If all values of the original type can be represented as an int, the value of the smaller type is converted to an int; otherwise, it is converted to an unsigned int.

这样,宏Convert(206) [((((unsigned short)206*(unsigned short)1000) + ((z) / (USHORT)2)) /(z))]将执行如下:

  1. “206”将被提升为有符号整数。
  2. “1000”将被提升为有符号整数。
  3. 将执行操作“206 * 1000”,结果也将是 signed int 类型 ->“206000”。
  4. z, 2 将被提升为有符号整数。
  5. 将执行运算 z / 2,结果类型为 signed int -> "14745 / 2" = "7372" 6."206000" + "7372" 将被执行,结果类型为 signed int -> 213372
  6. “213372”将除以“14745”得到“14”