如何在不损失结果范围的情况下将C中的uint转换为int

我想要两个无界整数之间的差,每个整数由uint32_t值表示,该值是取2 ^ 32为模的无界整数。如,例如,TCP序列号。请注意,与more restricted questions that do not allow wrapping around 0不同, modulo 2 ^ 32 表示形式可以环绕0。

假定基础无界整数之间的差在正常int的范围内。我想要这个带符号的差异值。换句话说,返回正常int范围内的值,该值等于两个uint32_t输入以模2 ^ 32为模的差。

例如,0 - 0xffffffff = 1,因为我们假定基础无界整数在int范围内。证明:如果A mod 2 ^ 32 = 0且B mod 2 ^ 32 = 0xffffffff,则(A = 0,B = -1)(mod 2 ^ 32)因此(AB = 1)(mod 2 ^ 32)和在int范围内,该模类具有单个代表1

我使用了以下代码:

static inline int sub_tcp_sn(uint32_t a,uint32_t b)
{
    uint32_t delta = a - b;

    // this would work on most systems
    return delta;

    // what is the language-safe way to do this?
}

这在大多数系统上都有效,因为它们对uintint都使用模2 ^ 32表示形式,并且普通的模2 ^ 32减法是在此处生成的唯一合理的汇编代码。

但是,我相信C标准仅在delta>=0时定义上述代码的结果。例如,在this question上,一个答案是:

  

如果我们将超出范围的值分配给带符号类型的对象,则   结果不确定。该程序似乎可以运行,可能会崩溃,   否则可能会产生垃圾值。

如何根据C标准从uintint进行模2 ^ 32转换?

注意:我希望答案代码不包含条件表达式,除非您可以证明它是必需的。 (在代码说明中进行案例分析就可以了。)

you6251545 回答:如何在不损失结果范围的情况下将C中的uint转换为int

必须有一个执行此操作的标准函数...但是与此同时:

#include <stdint.h>  // uint32_t
#include <limits.h>  // INT_MAX
#include <assert.h>  // assert

static inline int sub_tcp_sn(uint32_t a,uint32_t b)
{
    uint32_t delta = a - b;
    return delta <= INT_MAX ? delta : -(int)~delta - 1;
}

请注意,如果结果无法表示,则为UB,但问题是确定的。

如果系统具有64位long long类型,则可以轻松地自定义和检查范围:

typedef long long sint64_t;

static inline sint64_t sub_tcp_sn_custom_range(uint32_t a,uint32_t b,sint64_t out_min,sint64_t out_max)
{
    assert(sizeof(sint64_t) == 8);
    uint32_t delta = a - b;
    sint64_t result = delta <= out_max ? delta : -(sint64_t)-delta;
    assert(result >= out_min && result <= out_max);
    return result;
}

例如sub_tcp_sn_custom_range(0x10000000,-0xf0000000LL,0x0fffffffLL) == -0xf00000000

通过范围自定义,该解决方案在所有情况下都将范围损失降到最低,假设时间戳是线性的(例如,对0环绕没有特殊含义),并且可以使用单个64位类型。

本文链接:https://www.f2er.com/3156001.html

大家都在问