简体   繁体   English

C / C ++中没有溢出回绕的二进制加法

[英]Binary Addition without overflow wrap-around in C/C++

I know that when overflow occurs in C/C++, normal behavior is to wrap-around. 我知道,当C / C ++中发生溢出时,正常行为是绕回。 For example, INT_MAX+1 is an overflow. 例如, INT_MAX+1是一个溢出。

Is possible to modify this behavior, so binary addition takes place as normal addition and there is no wraparound at the end of addition operation ? 是否可以修改此行为,因此二进制加法会像普通加法一样发生,并且加法操作结束时没有回绕?

Some Code so this would make sense. 一些代码,所以这很有意义。 Basically, this is one bit (full) added, it adds bit by bit in 32 基本上,这是一个(全部)添加,它在32中一点一点添加

int adder(int x, int y)
{
    int sum;
    for (int i = 0; i < 31; i++)
    {
        sum = x ^ y;
        int carry = x & y;
        x = sum;
        y = carry << 1;
    }

    return sum;
}

If I try to adder(INT_MAX, 1); 如果我尝试adder(INT_MAX, 1); it actually overflows, even though, I amn't using + operator. 它实际上溢出了,即使我没有使用+运算符。

Thanks ! 谢谢 !

Overflow means that the result of an addition would exceed std::numeric_limits<int>::max() (back in C days, we used INT_MAX ). 溢出意味着相加的结果将超过std::numeric_limits<int>::max() (在C天之内,我们使用INT_MAX )。 Performing such an addition results in undefined behavior. 执行此类加法会导致未定义的行为。 The machine could crash and still comply with the C++ standard. 机器可能会崩溃,但仍然符合C ++标准。 Although you're more likely to get INT_MIN as a result, there's really no advantage to depending on any result at all. 尽管您更有可能获得INT_MIN的结果,但是完全依赖于任何结果实际上没有任何优势。

The solution is to perform subtraction instead of addition, to prevent overflow and take a special case: 解决方案是执行减法而不是加法,以防止溢出并采取特殊情况:

if ( number > std::numeric_limits< int >::max() - 1 ) { // ie number + 1 > max
    // fix things so "normal" math happens, in this case saturation.
} else {
    ++ number;
}

Without knowing the desired result, I can't be more specific about the it. 在不知道所需结果的情况下,我无法对其进行更具体的说明。 The performance impact should be minimal, as a rarely-taken branch can usually be retired in parallel with subsequent instructions without delaying them. 对性能的影响应该最小,因为很少使用的分支通常可以与后续指令同时退出而不会延迟它们。

Edit: To simply do math without worrying about overflow or handling it yourself, use a bignum library such as GMP . 编辑:要简单地做数学而不用担心溢出或自己处理它,请使用bignum库,例如GMP It's quite portable, and usually the best on any given platform. 它非常便携,通常是任何给定平台上最好的。 It has C and C++ interfaces. 它具有C和C ++接口。 Do not write your own assembly. 不要写你自己的组件。 The result would be unportable, suboptimal, and the interface would be your responsibility! 结果将是不可移植的,次优的,并且接口将由您负责!

不,您必须手动添加它们以检查是否溢出。

What do you want the result of INT_MAX + 1 to be? 您希望INT_MAX + 1的结果是什么? You can only fit INT_MAX into an int , so if you add one to it, the result is not going to be one greater. 您只能将INT_MAX装入一个int ,因此,如果将int加上一个,则结果将不会 INT_MAX ( Edit: On common platforms such as x86 it is going to wrap to the largest negative number: -(INT_MAX+1) . The only way to get bigger numbers is to use a larger variable. 编辑:在常见的平台上,例如x86,它将包装为最大的负数-(INT_MAX+1) 。获取更大数字的唯一方法是使用更大的变量。

Assuming int is 4-bytes (as is typical on x86 compilers) and you are executing an add instruction (in 32-bit mode), the destination register simply does overflow -- it is out of bits and can't hold a larger value. 假设int为4字节(如x86编译器中的典型值),并且您正在执行add指令(在32位模式下),则目标寄存器只是发生溢出 -它用完了位并且不能容纳更大的值。 It is a limitation of the hardware. 这是硬件的限制。

To get around this, you can hand-code, or use an aribitrarily-sized integer library that does the following: 要解决此问题,您可以手动编码,或使用执行以下操作的任意大小的整数库:

  • First perform a normal add instruction on the lowest-order words. 首先对最低顺序的字执行普通的add指令。 If overflow occurs, the Carry flag is set. 如果发生溢出,则进位标志置位。
  • For each increasingly-higher-order word, use the adc instruction, which adds the two operands as usual, but takes into account the value of the Carry flag (as a value of 1.) 对于每个高阶字,请使用adc指令,该指令照常将两个操作数相加,但要考虑到进位标志的值(值为1)。

You can see this for a 64-bit value here . 您可以在此处查看64位值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM