per*_*oud 5 c math language-lawyer
我想要两个无界整数之间的差异,每个整数都由一个uint32_t值表示,该值是取2 ^ 32为模的无界整数。如,例如,TCP序列号。请注意,模数2 ^ 32表示形式可以环绕0,这与更严格的问题不允许环绕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?
}
Run Code Online (Sandbox Code Playgroud)
这在大多数系统上都有效,因为它们对uint和都使用modulo-2 ^ 32表示形式int,并且正常的modulo-2 ^ 32减法是在此处生成的唯一合理的汇编代码。
但是,我相信C标准仅在的情况下定义上述代码的结果delta>=0。例如关于这个问题的一个答案说:
如果将超出范围的值分配给带符号类型的对象,则结果不确定。该程序似乎可以运行,崩溃或可能产生垃圾值。
应该如何从模2 ^ 32转换uint到int根据C标准做什么?
注意:我希望答案代码不包含条件表达式,除非您可以证明它是必需的。(在代码说明中进行案例分析就可以了)。
必须有一个标准函数可以做到这一点......但与此同时:
#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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,在结果不可表示的情况下是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;
}
Run Code Online (Sandbox Code Playgroud)
例如,sub_tcp_sn_custom_range(0x10000000, 0, -0xf0000000LL, 0x0fffffffLL) == -0xf00000000。
通过范围定制,该解决方案可以最大限度地减少所有情况下的范围损失,假设时间戳呈线性行为(例如,环绕 0 没有特殊含义)并且有可用的 64 位类型。
| 归档时间: |
|
| 查看次数: |
131 次 |
| 最近记录: |