将uint32_t转换为int32_t,既没有溢出的风险,也没有过度复杂的风险

Convert a uint32_t to an int32_t without either a risk of overflow or excessive complexity

本文关键字:没有过 复杂 溢出 转换 uint32 int32      更新时间:2023-10-16

在C++中,如何最好地通过包装将uint32_t快速转换为int32_t

一些尝试:

uint32_t y = UINT32_MAX;
int32_t x = (int32_t)y; // UB on overflow
int32_t x = *(int32_t*)&y; // does this violate strict aliasing?

int32_t x = (int32_t)y;不是溢出,也不是UB溢出是指算术运算产生的结果超出可表示值的范围。但是,转换不是算术运算。

这种情况是实现定义的行为。我所知道的所有实现都将行为定义为不更改表示。

请注意,此处不需要铸造。你可以写int32_t x = y;。实际上,这更简单,而且永远有效。如此多的代码依赖于此,以至于没有供应商会定义任何其他行为(并不是说他们有任何理由这样做)。


int32_t x = *(int32_t*)&y不是UB。它不违反严格别名,因为允许类型的有符号版本对无符号版本进行别名。该代码保证生成与相应的uint32_t具有相同表示的int32_t(即"包装",因为这些类型保证是2的补码)。

union { 
  int32_t i; 
  uint32_t u;
} u;
u.i = ...;
printf("%" PRIu32 "n", u.u);

This和memcpy(&uint_var, &int_var, sizeof uint_var)是在不调用未定义行为的情况下进行此类转换的两种标准方法。

另请参阅:

  • C99中是否未指定通过并集的类型punning,C11中是否已指定
#include <assert.h>
#include <limits.h>
#include <stdlib.h>
int makesigned(unsigned x) {
  if (x <= (unsigned) INT_MAX) {
    return (int) x;
  }
  /* assume 2's complement */
  if (x >= (unsigned) INT_MIN) {
    return 0 - (int)(-x);
  }
  abort();
  return 0;
}
int main(void) {
  assert(makesigned(0) == 0);
  assert(makesigned(INT_MAX) == INT_MAX);
  assert(makesigned(UINT_MAX) == -1);
  assert(makesigned(INT_MIN) == INT_MIN);
  return 0;
}
相关文章: