C运算溢出怎么办?

时间:2021-12-05 13:01:22

这个问题困扰了N久了,

最好的方式就是,事先考虑好范围,选择合适的类型.

当真正溢出时:
0000 --  0
0001 --  1
0010 --  2
0011 --  3
0100 --  4
0101 --  5
0110 --  6
0111 --  7
1000 --  -8
1001 --  -7
1010 --  -6
1011 --  -5
1100 --  -4
1101 --  -3
1110 --  -2
1111 --  -1

////////////////////////////////////////////////////////////////////////////
http://zhidao.baidu.com/question/2953609.html?fr=qrl
C 中调用积运算符之后做溢出检测已经太晚,但调用和运算符之后做检测则一点也不迟,
所以你可以通过对和运算结果的检测实现能检测溢出的积运算,因为 a * b 既是 a 个 b 的和:

-5000000 * 1374389539 等于 -(5000000 * 1374389539)。括号里是 5000000 个 1374389539 的和。

我把能检测溢出的和运算包在 add( ) 里,然后在 multiply( ) 里重复调用 add( )。

add( ) 怎么检测溢出?

和运算的结果若溢出将导致数值的环绕。上溢导致往下环绕,下溢导致往上环绕。

边界状况:
(对于有符号数)
(1)最轻微的上溢是 INT_MAX + 1 :结果是 INT_MIN。
(2)最严重的上溢是 INT_MAX + INT_MAX :结果是 -2。

(3)最轻微的下溢是 INT_MIN - 1 :结果是 INT_MAX。
(4)最严重的下溢是 INT_MIN - INT_MIN :结果是 0。

结论:

(1)所有上溢结果都小于任何一个操作数。
(2)所有下溢结果都大于任何一个操作数。

所以 add( ) 可以用结果和任意选定的一个参数判断溢出,并以落选的参数判断溢出的方向。

add( ) 无法以返回值举报溢出,所以采用 strtol( ) 的举报方法。

不同于 strtol( ) 的是,若没有溢出,add( ) 会把 0 赋值给 errno。
multiply( ) 在这方面跟 add( ) 一样。
 
 
 
#include<stdio.h>
#include<errno.h> /* errno, ERANGE */
 
 
/*
 * Returns the sum of a and b, with overflow and underflow check.
 * If overflow or underflow occurred, sets errno to ERANGE, else to 0.
 */
int add( int a, int b ) {

    int result = a + b;

    if( b > 0 && result > a || b <= 0 && result <= a )
        errno = 0;
    else
        errno = ERANGE;

    return result;
}
 
 
/*
 * Returns the product of a and b obtained through repeated call of add( ).
 * Affects errno exactly as add( ) does.
 */
int multiply( int a, int b ) {

    int sign_of_a = 1,
        result = 0,
             i = 0;

    /* Keep the sign of multiplier off the loop sentinel. */
    if( a < 0 ) {
        sign_of_a = -1;
        a *= -1;
    }

    /* Have to reset errno here because it won't be updated if a is 0. */
    errno = 0;

    while( i++ < a ) {
        result = add( result, b );
        if( errno == ERANGE )
            break;
    }

    return result * sign_of_a;
}
 
 
 
int main( ) {

    int result;

    /* Operands too huge: sure overflow. */
    result = multiply( -5000000, 1374389539 );
    if( errno == ERANGE )
        perror( "Uh oh" );
    else
        printf( "%d/n", result );


    /* Small operands: safe. */
    result = multiply( 49, -972 );
    if( errno == ERANGE )
        perror( "Uh oh" );
    else
        printf( "%d/n", result );

}
 
 
 
当然,应付溢出的最佳方法还是防范:充分了解数据的范围,选择恰当的变量类型。

也许你正考虑改用不需要你担心整数类型溢出的语言。考虑过 Python 吗?  

/////////////////////////////////////////////////////////////////////
http://topic.csdn.net/t/20021001/13/1067994.html
  C语言对整数是不检测溢出的,它只是简单把超出部分"切掉".  
    在结果不太在的情况下一个符号整数是否溢出可以判断它是否小于0  
  无符号整数是否溢出可以判断它是否小于原值.  
  如果结果较大,最好用容量大一点的类型(如long)然后判断它是否超出你要的范围.

无符号加法溢出判断:a+b   <   a  
  无符号减法溢出判断:a-b   >   a  
  无符号乘法溢出判断:程序必须保证乘法不溢出而不是算完后判断是否溢出。两个n位的整数相乘结果仍是n位的可能性有多大?一般都是用2n位来表示乘法结果。