关于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,原因我就不用讲了吧。。。