【问题标题】:How do I check if A+B exceed long long? (both A and B is long long) [duplicate]如何检查 A+B 是否超过 long long? (A和B都是长长的)[重复]
【发布时间】:2025-12-25 21:55:05
【问题描述】:

我有两个号码:AB。我需要在代码中的某处计算A+BAB 都是long long,它们可以是正面的或负面的

我的代码运行错误,我怀疑在计算A+B 时会出现问题。我只是想检查A+B 是否超过long long 范围。所以,任何方法都是可以接受的,因为我只用它来调试。

【问题讨论】:

  • if ((A<0 && B<0 && A+B>0) || (A>0 && B>0 && A+B<0)){/*Overflow*/} 只有当 A 和 B 的符号相同时,才会溢出。如果他们这样做了,并且 A+B 没有相同的符号,那么你就有溢出问题。在其他新闻中,这基本上是最快的方法,因为它在恒定时间内执行,并且只执行一次加法。
  • 您应该将其发布为答案。顺便说一句,当A=B=0x8000000000000000,然后A+B=0 并且您的代码不起作用时,有一个极端情况:p
  • @FrankieTheKneeMan 整数溢出是未定义的行为。
  • @John 这就是我将其作为评论提交的原因。我的答案适用于大多数编译器,对于第一步调试来说已经足够了。
  • @AlexeyFrunze 你提到的所谓的副本处理无符号类型,在所有情况下都以明确定义的方式表现。在某些情况下,Sigend 整数的行为并不明确。

标签: c++ long-long


【解决方案1】:

只有当两个数字具有相同的符号时,才可能发生溢出。如果两者都是正数,那么在数学上A + B > LLONG_MAX 或等效B > LLONG_MAX - A 时就会溢出。由于右手边是非负的,后一个条件已经暗示了B > 0。类似的论点表明,对于否定的情况,我们也不需要检查B 的符号(感谢Ben Voigt 指出对B 的符号检查是不必要的)。然后你可以检查

if (A > 0) {
    return B > (LLONG_MAX - A);
}
if (A < 0) {
    return B < (LLONG_MIN - A);
}
return false;

检测溢出。由于初始检查,这些计算不会溢出。

检查A + B 结果的符号将保证溢出整数计算的环绕语义。但是有符号整数的溢出是未定义的行为,即使在实现了回绕行为的 CPU 上,编译器也可能假设没有发生未定义的行为,并在实现时完全删除溢出检查。因此,在 cmets 中建议的对该问题的检查是非常不可靠的。

【讨论】:

  • 不仅检查符号不能保证工作,而且优化器正在使用这种缺乏保证(例如,我刚刚检查了 gcc 将 a + 42 &gt; a 优化为 true)。
  • 如果你将它包装在一个表达式中,而不是左右扔掉return,这将更具可读性。比如return a &lt; 0 != b &lt; 0 || (a &lt; 0 ? b &gt; LLONG_MIN - a : b &lt; LLONG_MAX - a);
  • @JamesKanze 我发现分解成不同的案例更具可读性,但这当然只是我的偏好。
  • FWIW,我同意 Daniel 的风格选择。我不认为 if 中的返回是“隐藏的”,尤其是在 7 行函数中。
  • 我也认为它更清晰地分为几个单独的步骤,因为它明确地分解了每个案例。由于对多重回报的宗教敌意,教条地把所有东西都塞进一个单一的声明中,这总是对任何人都不利;并用retVal = blah 替换所有中间return blah 行同样糟糕。
【解决方案2】:

类似于以下内容:

long long max = std::numeric_limits<long long>::max();
long long min = std::numeric_limits<long long>::min();

if(A < 0 && B < 0) 
    return B < min - A;
if(A > 0 && B > 0)
    return B > max - A;

return false;

我们可以这样推理:

  • 如果AB 符号相反,则它们不会溢出 - 大于零的值需要大于max,或者小于零的值需要小于@987654325 @。

  • 在其他情况下,简单代数就足够了。 A + B &gt; max =&gt; B &gt; max - A 如果它们都是正数,则会溢出。否则,如果它们都是负数,A + B &lt; min =&gt; B &lt; min - A

【讨论】:

  • B &gt; 0B &lt; 0 测试是多余的。
【解决方案3】:

另外,如果你只是将它用于调试,你可以使用下面的 'hack' 直接从最后一个操作中读取溢出位(假设你的编译器/cpu 支持这个):

int flags;
_asm {
    pushf       // push flag register on the stack
    pop flags   // read the value from the stack
}
if (flags & 0x0800) // bit 11 - overflow
    ...

【讨论】:

  • 进位标志对于无符号数很有趣。对于带符号的数字,您必须检查溢出标志。
【解决方案4】:

屏蔽符号,转换为无符号值,然后执行加法。如果它高于1 &lt;&lt; (sizeof(int) * 8 - 1),那么你就有溢出了。

int x, y;
if (sign(x) == sign(y)){
    unsigned int ux = abs(x), uy = abs(y);    
    overflow = ux + uy >= (1 << (sizeof(int) * 8 - 1));
}

更好的是,让我们编写一个模板:

template <typename T> 
bool overflow(signed T x, signed T y){
    unsigned T ux = x, uy = y;
    return ( sign(x) == sign(y) && (ux + uy >= (1 << (sizeof(T) * 8 - 1)));
}

【讨论】:

    最近更新 更多