有符号数与无符号数相加时如何判断溢出
我试图在将有符号偏移量添加到无符号位置时检测溢出
uint32 position;
int32 offset; // it could be negative
uint32 position = position+offset;
如何检查结果是上溢还是下溢?
我想到了一个丑陋的方法,但不确定它的正确性。
- 下溢:
偏移量< 0 &&位置+偏移>=位置
- 溢出:
偏移> 0 && position + offset <=position
我也想知道是否有更优雅的方法来做到这一点。
更新:
如果偏移量很长,最好的解决方案是什么?
uint32 position;
long offset; // it could be negative
uint32 position = position+offset;
I'm trying to detect the overflow when adding a signed offset to an unsigned position
uint32 position;
int32 offset; // it could be negative
uint32 position = position+offset;
How can I check whether the result is overflow or underflow?
I have thought of an ugly way but not sure of its correctness.
- underflow:
offset < 0 && position + offset >= position
- overflow:
offset > 0 && position + offset <= position
And I'm also wondering if there's a more elegant way to do it.
Update:
What's the best solution if offset is long?
uint32 position;
long offset; // it could be negative
uint32 position = position+offset;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您的测试是正确的。我现在没有看到更优雅的方式,也许没有。
为什么条件是正确的:
uint32_t
上的算术是模 2^32 的算术。从int32_t
到uint32_t
的转换通常是位模式的重新解释(无论如何,正如 @caf 指出的那样,这里它是模 2^32 的减少,所以它肯定是作品)。将position
和offset
视为任意精度整数。溢出当且仅当位置 + 偏移量 >= 2^32
。但是offset < 2^31,所以位置+偏移量
position + 2^31
,小于position + 2^32
,即减少到position
模 2^32 的下一个值,因此uint32_t
,然后位置+偏移量
位置
。另一方面,如果offset > 0
和位置 + 偏移量
。位置
,显然发生了溢出。当且仅当位置 + 偏移量时才会发生下溢。 0
作为数学整数。由于offset >= -2^31,类似的推理表明当且仅当offset <= -2^31 时才会发生下溢。 0 &&位置+偏移量>位置Your test(s) is (are) correct. I don't see a more elegant way right now, perhaps there isn't.
Why the conditions are correct: arithmetic on
uint32_t
is arithmetic modulo 2^32. Conversion fromint32_t
touint32_t
is normally the reinterpretation of the bit-pattern (in any case, as @caf pointed out, it's reduction modulo 2^32 here, so it definitely works). Regardposition
andoffset
as arbitrary precision integers. Overflow happens if and only ifposition + offset >= 2^32
. Butoffset < 2^31
, soposition + offset < position + 2^31
, which is less thanposition + 2^32
, the next value that reduces toposition
modulo 2^32, so asuint32_t
, thenposition + offset < position
. On the other hand, ifoffset > 0
andposition + offset < position
, evidently overflow has occurred. Underflow happens if and only ifposition + offset < 0
as mathematical integers. Sinceoffset >= -2^31
, similar reasoning shows that underflow has occurred if and only ifoffset < 0 && position + offset > position
.以下函数在将 int32_t 添加到 uint32_t 时检查上溢/下溢。它还包含一些测试用例作为正确性的证据。
The following function check for overflow/underflow when adding an int32_t to an uint32_t. It also contains some test cases as evidence of correctness.
具体方法如下:
u 后缀是为了确保 0xFFFFFFFF 常量是无符号类型(没有后缀的十六进制常量可以是有符号的或无符号的,具体取决于值以及编译器如何定义 int、long 和 long long),因此< 左侧的表达式是未签名的。可能不需要,但我有点厌倦弄清楚是否不需要。肯定不会痛。
(uint32) 强制转换是为了关闭编译器,因为编译器可能认为我们正在做一些愚蠢的事情(比较有符号与无符号)。
更新:如果 int32 具有 2 的补码表示形式且 offset = -0x80000000,则允许表达式
-offset
引发实现定义的信号,甚至可能导致未定义的行为C 标准(请参阅6.3.1.3 有符号和无符号整数
和7.20.6.1 abs、labs 和 llabs 函数
C99),但实际上这种情况永远不会发生,因为在大多数平台上,否定是作为一个简单的指令(或几个指令)实现的,不会在 CPU 中引发任何异常/中断/陷阱/事件,并且生成额外的代码没有什么价值检查这种边缘情况,特别是因为整数用 2 的补码表示,并且 -0x80000000 的绝对值无论如何都是 0x80000000,这可能很方便(例如对于绝对值计算)。 CPU 不太关心有符号整数,甚至对两者使用相同的加法和减法指令(这是 2 的补码的好处),并且很少关心整数溢出,因为它们在软件中经常发生,并且是一种生活。请注意这一点,但不要担心。请了解这些是如何在 Microsoft 的 SafeInt for C++ 中实现的(代码,简介,在 MSDN 上,视频)和 C 版 IntSafe (简介 + 代码,在 MSDN 上)。
Here's how you can do it:
The u suffix is to ensure that the 0xFFFFFFFF constant is of unsigned type (hex constants without suffixes can be signed or unsigned, depending on value and how your compiler defines int, long and long long) and therefore the expression to the left of < is unsigned. It might not be needed, but I'm a bit tired to figure out if it's not. It won't hurt for sure.
The (uint32) casts are to shut up the compiler that may think we're doing something stupid (comparing signed with unsigned).
UPDATE: If int32 has a 2's complement representation and offset = -0x80000000, the expression
-offset
is allowed to raise an implementation-defined signal or maybe even cause undefined behavior per the C standard (see sections6.3.1.3 Signed and unsigned integers
and7.20.6.1 The abs, labs and llabs functions
of C99), but practically this never occurs because on most platforms negation is implemented as a simple instruction (or a few) that doesn't raise any exception/interrupt/trap/event in the CPU and there's little value in generating extra code to check for this edge case, especially since integers are represented in 2's complement code and the absolute value of -0x80000000 is 0x80000000 anyway, which may be handy (e.g. for absolute value calculations). The CPU doesn't care much about signed integers and even uses the same addition and subtraction instructions for both (this is the benefit of the 2's complement) and it rarely cares about integer overflows, because they occur routinely in software and are a way of life. Be aware of it, but don't sweat it.Please see how these are implemented in Microsoft's SafeInt for C++ (Code, Intro, On MSDN, Video) and IntSafe for C (Intro + Code, On MSDN).