代码之家  ›  专栏  ›  技术社区  ›  CPlus

用C中的有符号整数执行缩小类型转换最安全、定义最明确的方法是什么?

  •  0
  • CPlus  · 技术社区  · 2 年前

    我已阅读: What happens when I assign long int to int in C?

    int64_t x;
    int32_t y;
    
    y = (int32_t)x;
    

    根据答案和C标准,例如,如果我试图分配 signed long signed int 并且该值不适合、结果是实现定义的或者产生实现定义的信号。

    为了解决这种定义不清的行为,我首先考虑使用无符号类型:

    y = (int32_t)(uint32_t)(uint64_t)x;
    

    但这也不好,因为就像将较大的整数类型转换为较小的整数类型一样,如果值不合适,将无符号整数转换为有符号整数将导致实现定义的值或信号。

    我想以一种不调用实现定义的行为的方式执行缩小转换,如无符号到有符号的转换,或较大的有符号整数到较小的有符号的整数的转换,并且只保留位表示(只要符号表示如预期的那样,这应该会给出非常可预测和定义良好的结果)。有什么方法可以用石膏做这个吗?我必须使用 union 像这样?

    union Cast {
        int32_t i32;
        int64_t i64;
    };
    y = ((union Cast){.i64 = x}).i32;
    

    这样行得通吗(即保证 y 简单地包含精确的位模式作为的低32位 x )? 作为最后的手段 memcpy() 必须使用,这会起作用吗?

    编辑 原来是用上面的 协会 这将是不可移植的,因为它是获得高32位还是低32位取决于系统是大字节序还是小字节序。但是,如果我使用这个:

    union Cast {
        int32_t i;
        uint32_t u;
    };
    y = ((union Cast){.u = (uint32_t)x}).i;
    

    那是便携式的吗?

    笔记 问这个问题是出于好奇,而且很多时候,出于各种原因,我想安全地截断有符号整数的位。

    0 回复  |  直到 2 年前
        1
  •  1
  •   Eric Postpischil    2 年前

    我想以一种只保留位表示的方式执行缩小转换(只要符号表示如预期,这就应该给出非常可预测和定义良好的结果)。

    好吧,这是正确的,但它缺少了一些东西:你没有说预期的符号表示。编写规范的一个专家提示是实际指定您想要的内容。

    假设你想要两个补码,因为它是最流行的表示方案。等价地,给定某个整数 x 您想转换为签名的 N -位整数格式,您想要一个结果 y 与一致 x 模2 N 有2个 N 1. ¤ y <2. N 1. .

    这个 int N _t 类型保证使用twos补码并且没有填充位(C 2018 7.20.1.1 1)。所以,如果你能得到 x 变成 int N _t ,你完了。(我还将注意到,在C 2018 6.2.6.2 2中,给定有符号整数类型及其相应的无符号整数类型,标准要求其相应的值位表示相同的值。)

    您不能依赖具有更宽类型的并集,因为C标准没有指定存储中字节的顺序,因此仅从C标准不知道更宽类型中哪些字节包含其低位。我们可以通过将值转换为 uint N _t 第一从无符号类型范围外的整数到无符号类型的转换完全由C标准定义;它封装模2 N ,在[0,2中产生结果 N ).

    在这里,我们执行转换,并在复合文字中使用并集,用twos补码重新解释其结果:

    (union { uint N _t u; int N _t i; }) {x} .i

    虽然问题中没有提到C++,但我要注意的是,通过并集重新解释并不是由C++标准定义的。另一种选择是复制字节:

    uintN_t u = x;
    intN_t i;
    memcpy(&i, &u, sizeof i);
    

    这两者都可移植到支持所需固定宽度类型的任何实现中。

    这也可以使用算术而不是重新解释字节来完成。假设我们正在从某种更广泛的类型转换为 M 位。首先我们可以选到 无符号整型 N _t 以获得低位。然后我们可以调整bit表示的值 N 1. ((uint N _t) x ^ ((int M _t) 1 << N -1)) - ((int M _t) 1 << N ) 。该算法在中进行了解释 this answer .

    我们还可以使用:

    uintN_t u = x;
    intN_t i = u - (intM_t) u >> N-1 << N;
    

    该算术在中进行了解释 this answer .