Add integers safely, and prove the safety

安稳与你 提交于 2019-12-23 16:24:17

问题


Programming course assignment asks to

  • write a (safe) function that adds two integers, and
  • show that the function is safe.

The following code represents my solution. I am not an expert on the C standard (or on formal verification methods). So I would like to ask: Are there better (or different) solutions?

Thank you

#include <limits.h>

/*
      Try to add integers op1 and op2.
      Return
        0 (success) or
        1 (overflow prevented).
      In case of success, write the sum to res.
*/

int safe_int_add(int * res,
                 int op1,
                 int op2) {
  if (op2 < 0) {

    /**  We have: **********************************************/
    /*                                                         */
    /*          0  >     op2                                   */
    /*          0  <   - op2                                   */
    /*    INT_MIN  <   - op2 + INT_MIN                         */
    /*    INT_MIN  <           INT_MIN - op2                   */
    /*    INT_MIN  <=          INT_MIN - op2                   */
    /*                                                         */
    /**  Also, we have: ****************************************/
    /*                                                         */
    /*              op2  >=    INT_MIN                         */
    /*            - op2  <=  - INT_MIN                         */
    /*    INT_MIN - op2  <=  - INT_MIN + INT_MIN               */
    /*    INT_MIN - op2  <=  0                                 */
    /*    INT_MIN - op2  <=  INT_MAX                           */
    /*                                                         */
    /**  Hence, we have: ***************************************/
    /*                                                         */
    /*    INT_MIN  <=  INT_MIN - op2  <=  INT_MAX              */
    /*                                                         */
    /*    i.e. the following subtraction does not overflow.    */
    /*                                                         */
    /***********************************************************/

    if (op1 < INT_MIN - op2) {
      return 1;
    }

    /**  We have: *********************************/
    /*                                            */
    /*    INT_MIN - op2  <=  op1                  */
    /*    INT_MIN        <=  op1 + op2            */
    /*                                            */
    /**  Also, we have: ***************************/
    /*                                            */
    /*          op2  <   0                        */
    /*    op1 + op2  <   op1                      */
    /*    op1 + op2  <   INT_MAX                  */
    /*    op1 + op2  <=  INT_MAX                  */
    /*                                            */
    /**  Hence, we have: **************************/
    /*                                            */
    /*    INT_MIN  <=  op1 + op2  <=  INT_MAX     */
    /*                                            */
    /*    i.e. the addition does not overflow.    */
    /*                                            */
    /**********************************************/

  }
  else {

    /**  We have: **********************************************/
    /*                                                         */
    /*              op2  >=  0                                 */
    /*            - op2  <=  0                                 */
    /*    INT_MAX - op2  <=  INT_MAX                           */
    /*                                                         */
    /**  Also, we have: ****************************************/
    /*                                                         */
    /*              INT_MAX  >=    op2                         */
    /*            - INT_MAX  <=  - op2                         */
    /*    INT_MAX - INT_MAX  <=  - op2 + INT_MAX               */
    /*                    0  <=  - op2 + INT_MAX               */
    /*                    0  <=          INT_MAX - op2         */
    /*              INT_MIN  <=          INT_MAX - op2         */
    /*                                                         */
    /**  Hence, we have: ***************************************/
    /*                                                         */
    /*    INT_MIN  <=  INT_MAX - op2  <=  INT_MAX              */
    /*                                                         */
    /*    i.e. the following subtraction does not overflow.    */
    /*                                                         */
    /***********************************************************/

    if (op1 > INT_MAX - op2) {
      return 1;
    }

    /**  We have: *********************************/
    /*                                            */
    /*    op1        <=  INT_MAX - op2            */
    /*    op1 + op2  <=  INT_MAX                  */
    /*                                            */
    /**  Also, we have: ***************************/
    /*                                            */
    /*          0  <=  op2                        */
    /*        op1  <=  op2 + op1                  */
    /*    INT_MIN  <=  op2 + op1                  */
    /*    INT_MIN  <=        op1 + op2            */
    /*                                            */
    /**  Hence, we have: **************************/
    /*                                            */
    /*    INT_MIN  <=  op1 + op2  <=  INT_MAX     */
    /*                                            */
    /*    i.e. the addition does not overflow.    */
    /*                                            */
    /**********************************************/

  }

  *res = op1 + op2;
  return 0;
}

回答1:


OP's approach is optimally portably staying within type int as well as safe - no undefined behavior (UB) with any combination of int. It is independent of a particular int format (2's complement, 2's complement, sign-magnitude).

In C, int overflow/(underflow) is undefined behavior. So code, if staying with int, must determine overflow potential before-hand. With op1 positive, INT_MAX - op1 cannot overflow. Also, with op1 negative, INT_MIN - op1 cannot overflow. So with edges properly calculated and tested, op1 + op2 will not overflow.

// Minor re-write:
int safe_int_add(int * res, int op1, int op2) {
  assert(res != NULL);
  if (op1 >= 0) { 
    if (op2 > INT_MAX - op1) return 1;
  } else { 
    if (op2 < INT_MIN - op1) return 1;
  }
  *res = op1 + op2;
  return 0;
}

See also

If a know wider type is available, code could use

int safe_int_add_wide(int * res, int op1, int op2) {
  int2x sum = (int2x) op1 + op2;
  if (sum < INT_MIN || sum > INT_MAX) return 1;
  *res = (int) sum;
  return 0;
}

Approaches using unsigned, etc. first need to qualify that UINT_MAX >= INT_MAX - INT_MIN. This is usually true, but not guaranteed by the C standard.




回答2:


This is how I would do it:

If the input arguments have different signs then the result is always computable without any risk of overflow.

If both input arguments are negative, then compute -safe_int_add(res, -op1, -op2);. (You'll need to check that op1 or op2 are not the largest negative in 2s compliment).

The function that needs thought is the one that adds two positive numbers: convert your two inputs to unsigned types. Add those. That is guaranteed to not overflow the unsigned type since you can store (at least) twice as large values in an unsigned int than in a signed int (exactly twice for 1s compliment, one more than that for 2s compliment).

Then only attempt a conversion back to signed if the unsigned value is small enough.




回答3:


You can look at the implementation of JDK 8, which has a fine reference to the book Hacker's Delight from Henry S. Warren, Jr. here:

http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/b971b51bec01

public static int addExact(int x, int y) {
    int r = x + y;
    // HD 2-12 Overflow iff both arguments have the opposite sign of the result
    if (((x ^ r) & (y ^ r)) < 0) {
        throw new ArithmeticException("integer overflow");
    }
    return r;
}

In my version of the book, it is chapter 2-13 though. There you can find a lengthy explanation about the whole issue.



来源:https://stackoverflow.com/questions/30371505/add-integers-safely-and-prove-the-safety

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!