【发布时间】:2015-06-11 03:11:43
【问题描述】:
我想解决一些关于溢出的问题。我有一些数据使用int来存储,数据不会导致溢出但计算中间可能会导致溢出。
比如我要存储正方形的对角线,边长是50000,所以对角线是70710,边和对角线都远小于INT_MAX,但是为了计算,aa+b sqrt(aa+bb) 中的 b 会导致溢出。
我想遵循“只使用 int”的规则,所以我可能每次都需要强制转换每个变量:
int f=(long)a+(long)b*(long)c/(long)d-(long)e;
但是每次add(long)都会影响可读性,我测试一下哪个操作可能会导致溢出,哪个可能有自动转换:
#include <sstream>
int main(){
int a=rand();
int b=a;
printf("%d\n",a);
printf("%d\n",INT_MAX);
printf("\n");
printf("%d\n",INT_MAX+a-b);
printf("%d\n",INT_MAX-b+a);
printf("%d\n",a+INT_MAX-b);
printf("%d\n",a-b+INT_MAX);
printf("%d\n",-b+a+INT_MAX);
printf("%d\n",-b+INT_MAX+a);
printf("\n");
printf("%d\n",INT_MAX*a/b);
printf("%d\n",INT_MAX/b*a);
printf("%d\n",a*INT_MAX/b);
printf("%d\n",a/b*INT_MAX);
printf("\n");
printf("%ld\n",(long)INT_MAX*a/b);
printf("%ld\n",INT_MAX*a/(long)b);
return 0;
}
输出是:
16807
2147483647
2147483647
2147483647
2147483647
2147483647
2147483647
2147483647
127772
2147480811
127772
2147483647
2147483647
127772
我使用 rand() 来确保没有编译时间计算,我发现 + 和 - 对于不同的 INT_MAX、+a 和 -b 序列的结果是相同的,但对于 *a 和 /b 则不是。
我还发现甚至使用强制转换,(long)INT_MAXa/b 是正常的,但 INT_MAXa/(long)b 不是。
我猜对于+和-,如果结果小于INT_MAX,即使计算中间(例如:INT_MAX+a-b中的INT_MAX+a)也不会导致溢出,但是对于*和/,溢出中间会影响结果,对吗?
同样对于*和/,我guest操作是从左侧开始的,所以铸造需要从左侧开始(例如:(long)INT_MAX*a/b),是不是也对?
所以,如果我的数据没有导致溢出但计算可能会导致溢出,是
int f=a+b*c/d-e;
只需要改写为
int f=a+(long)b*c/d-e;
?
【问题讨论】:
-
1.有符号整数溢出是未定义的行为。它可能看起来像是在某一时刻有效,但在一周内开始中断而没有任何变化。 2. long 可能与 int 大小相同,但仍会溢出(我知道 long 和 int 在 32 位 linux 以及 32 位和 64 位窗口上的大小相同)。
-
你只需要像
int f = a + b*(long)c/d - e;这样的一次转换(仅供参考,适当地添加空格和换行符也有助于提高可读性),因为操作中的另一个操作数将相应地自动提升。你需要一个比int更宽的类型,long不能保证 -
INT_MAX+a产生一个适合int的临时值,该值本身将被解释为负数。请参阅 ideone.com/mOiRS6 了解您的代码的略微修改版本。 -
@RSahu:不,
INT_MAX+a的评估产生了一个不适合结果类型int的值,这就是你得到未定义行为的原因。 -
@BenVoigt,理论上同意你的看法。但是,如果结果可以在特定运行时环境中多次运行复制,我会尝试理解为什么会发生这种情况。
标签: c++ integer-overflow