int64与longint使用的注意事项

关于longint和int64,相信OIer们并不陌生,这两个数据类型可以这样定义

longint=-1 shl 31..(1 shl 31)-1;

int64=-1 shl 63..(1 shl 63)-1;

分别占用4个和8个字节

与之对应的无符号类型为word和qWord,较之并不常用

网上有不少人说int64不好使,有漏洞,容易造成运行时错误,竞赛时最好不要用int64

但事实上,int64是我仅次于longint的最常用的数据类型。int64的问题出在哪呢?

先看一个例子

program ex1;

var
a,b,c,d:longint;

begin
a:=maxLongint;
b:=maxLongint;
c:=10;
d:=10;
if a+b<b+c then writeln(‘a+b<c+d’)
else writeln(‘a+b>=c+d’);
end.

按常理,程序应当输出a+b>=c+d,但事实上是:

Runtime error 215 at $004013BA
$004013BA
$00406180

算数上溢错误,为什么会这样呢?

因为Pascal在比较a+b<c+d时,会先算出a+b的值,由于a=b=maxLongint,两者相加超过了maxLongint,自然会溢出。

我们再看一个例子

program ex2;

var
a,b,c,d:longint;
e,f:int64;

begin
a:=maxLongint;
b:=maxLongint;
e:=a+b;
c:=10;
d:=10;
f:=c+d;
if e<f then writeln(‘a+b<c+d’)
else writeln(‘a+b>=c+d’);
end.

输出是:

Runtime error 215 at $004013A6
$004013A6
$004061B0

错误在f:=c+d;这一句,按理说maxLongint+maxLongint应当可以用int64储存,事实上确实这样:

program ex3;
var
a:int64;

begin
a:=maxLongint*2;
writeln(a);
end.

输出4294967294

但为什么ex2会出错呢?

原因是在执行e:=a+b;这一句时,Pascal先把两个longint类型的值相加(然而此时的上界为maxLongint,自然会出现的215错误),然后再通过类型转换转存到int64的e变量中。

那么怎么实现才不出错呢?

program ex4;

var
a,b,c,d:longint;
e,f:int64;

begin
a:=maxLongint;
b:=maxLongint;
e:=a;
e:=e+b;
c:=10;
d:=10;
f:=c;
f:=f+d;
if e<f then writeln(‘a+b<c+d’)
else writeln(‘a+b>=c+d’);
end.

输出a+b>=c+d

在e:=a;中a的值被强制转换成int64类型,存入e中,在执行e:=e+b;时,是一个int64类型的值与longint类型的值相加,Pascal会先将longint值转换成int64,,加的时候上界也不再是maxLongint,而是maxInt64=(1 shl 63)-1,不会出错。

还有一种写法不会出错:c:=int64(a)+int64(b);,先对a,b进行类型转换,再相加。(FP2.2.4和Delphi编译通过,但可能低版本的FP中不允许这样用,比如FP For NOI)

至此,似乎int64“不好使”的原因已经找到了——类型转换。

但请再看一个例子:

program ex5;
var
a,b:integer;
c:longint;

begin
a:=maxint;
b:=maxint;
c:=a+b;
writeln(c);
end.

按照前面所讲,似乎应该是215错误

但结果是输出:65534

这又是为什么呢?

在int64的百度百科中有这么一句:

警告 在 32 位 Intel 计算机上分配 64 位值不是原子操作;即该操作不是线程安全的。这意味着,如果两个人同时将一个值分配给一个静态 Int64 字段,则该字段的最终值是无法预测的。

OI中不会用到多线程,所以不用担心这个问题

但这却提示了我们,32位——也就是4个字节——一个longint,可能是Pascal的处理单位,或者说在进行各种运算的时候,上界永远不会低于maxLongint,所以ex5是合法的。或者说,longint以内的操作不需运算符重载,而int64的运算是运算符重载而来的(也许比这更复杂),而int64只是一个记录:

type

int64=record

high,low:longint

end;

(以上纯属虚构。。。)

那么,如果在64为电脑上运行上面几个215错误的程序又会是什么结果呢?我不知道,如果有可能我会去试试。(期待Delphi的64bit版)

 

最后,传授一个经验(教训),最好不要用maxLongint作为正无穷,可以用:

const maxLong=maxLongint shr 1;(或2)

原因例如进行Floyd,if a[i,k]+a[k,j]<a[i,j],可能会215,原因我就不用讲了吧。。。

文章评论: